1 line
18 KiB
JSON
1 line
18 KiB
JSON
{"repo": "abhishekgujar28/nexus", "files": {"nexus/server/src/core/config.ts": {"ba7051860879fec7b9c33ae38b16665dc9186b93": {"t": 1785745609.4727852, "n": 4829, "h": "f1bf60209a0320a9", "c": "import 'dotenv/config';\n\nconst bool = (v: string | undefined): boolean => v === 'true' || v === '1';\n\nconst cleanEnvKey = (v: string | undefined): string => {\n if (!v) return '';\n const trimmed = v.trim();\n if (/^(your-.*|change-this-.*|placeholder)$/i.test(trimmed)) return '';\n return trimmed;\n};\n\nexport const config = {\n env: process.env.NODE_ENV || 'development',\n port: parseInt(process.env.PORT || '5000'),\n frontendUrl: process.env.FRONTEND_URL || 'http://localhost:5173',\n mongoUri: process.env.MONGODB_URI || 'mongodb://localhost:27017/nexus',\n redisUrl: process.env.REDIS_URL || 'redis://localhost:6379',\n redis: {\n host: process.env.REDIS_HOST || 'localhost',\n port: parseInt(process.env.REDIS_PORT || '6379', 10),\n password: process.env.REDIS_PASSWORD || undefined,\n db: parseInt(process.env.REDIS_DB || '0', 10),\n url: process.env.REDIS_URL || 'redis://localhost:6379',\n },\n jwt: {\n // No hardcoded fallbacks — validated at startup by validateConfig().\n secret: process.env.JWT_SECRET || '',\n refreshSecret: process.env.JWT_REFRESH_SECRET || '',\n expiresIn: process.env.JWT_EXPIRES_IN || '15m',\n refreshExpiresIn: process.env.JWT_REFRESH_EXPIRES_IN || '7d',\n },\n geminiApiKey: cleanEnvKey(process.env.GEMINI_API_KEY),\n defaultAiProvider: process.env.DEFAULT_AI_PROVIDER || 'openrouter',\n ai: {\n openrouter: {\n apiKey: cleanEnvKey(process.env.OPENROUTER_API_KEY),\n baseUrl: process.env.OPENROUTER_BASE_URL || 'https://openrouter.ai/api/v1',\n },\n gemini: {\n apiKey: cleanEnvKey(process.env.GEMINI_API_KEY),\n },\n openai: {\n apiKey: cleanEnvKey(process.env.OPENAI_API_KEY),\n },\n anthropic: {\n apiKey: cleanEnvKey(process.env.ANTHROPIC_API_KEY),\n },\n groq: {\n apiKey: cleanEnvKey(process.env.GROQ_API_KEY),\n },\n deepseek: {\n apiKey: cleanEnvKey(process.env.DEEPSEEK_API_KEY),\n },\n together: {\n apiKey: cleanEnvKey(process.env.TOGETHER_API_KEY),\n },\n },\n serperApiKey: cleanEnvKey(process.env.SERPER_API_KEY),\n githubToken: cleanEnvKey(process.env.GITHUB_TOKEN),\n semanticScholarApiKey: cleanEnvKey(process.env.SEMANTIC_SCHOLAR_API_KEY),\n ieeeApiKey: cleanEnvKey(process.env.IEEE_XPLORE_API_KEY),\n chromaUrl: process.env.CHROMA_URL || 'http://localhost:8000',\n rag: {\n chunkSize: parseInt(process.env.RAG_CHUNK_SIZE || '1200'),\n chunkOverlap: parseInt(process.env.RAG_CHUNK_OVERLAP || '150'),\n topK: parseInt(process.env.RAG_TOP_K || '8'),\n maxContextChars: parseInt(process.env.RAG_MAX_CONTEXT_CHARS || '12000'),\n },\n research: {\n workerConcurrency: parseInt(process.env.RESEARCH_WORKER_CONCURRENCY || '2'),\n jobAttempts: parseInt(process.env.RESEARCH_JOB_ATTEMPTS || '2'),\n providerTimeoutMs: parseInt(process.env.PROVIDER_TIMEOUT_MS || '15000'),\n maxSourcesPerProvider: parseInt(process.env.MAX_SOURCES_PER_PROVIDER || '10'),\n },\n copilot: {\n historyWindow: parseInt(process.env.COPILOT_HISTORY_WINDOW || '10'),\n },\n vectorStore: process.env.VECTOR_STORE || 'memory', // 'chroma' | 'memory'\n} as const;\n\n/**\n * Fail-fast validation of required environment. Called once at startup\n * (and by the worker) before anything security-sensitive runs.\n */\nexport const validateConfig = (): void => {\n const missing: string[] = [];\n if (!config.jwt.secret) missing.push('JWT_SECRET');\n if (!config.jwt.refreshSecret) missing.push('JWT_REFRESH_SECRET');\n\n if (missing.length > 0) {\n throw new Error(\n `Missing required environment variables: ${missing.join(', ')}. ` +\n `Set them (see .env.example) before starting the server.`\n );\n }\n\n if (isNaN(config.redis.port) || config.redis.port <= 0 || config.redis.port > 65535) {\n throw new Error(`Invalid REDIS_PORT: \"${process.env.REDIS_PORT}\". Must be a valid port number (1-65535).`);\n }\n\n if (config.redis.url && !/^rediss?:\\/\\//i.test(config.redis.url)) {\n throw new Error(`Invalid REDIS_URL format: \"${config.redis.url}\". Must start with redis:// or rediss://`);\n }\n\n if (config.env === 'production') {\n if (config.jwt.secret.length < 32 || config.jwt.refreshSecret.length < 32) {\n throw new Error('JWT secrets must be at least 32 characters in production.');\n }\n if (config.jwt.secret === config.jwt.refreshSecret) {\n throw new Error('JWT_SECRET and JWT_REFRESH_SECRET must differ.');\n }\n }\n};\n\nexport const providerStatus = {\n gemini: () => ({ name: 'Gemini', configured: !!config.geminiApiKey }),\n serper: () => ({ name: 'Serper', configured: !!config.serperApiKey }),\n github: () => ({ name: 'GitHub', configured: !!config.githubToken }),\n arxiv: () => ({ name: 'arXiv', configured: true }),\n semanticScholar: () => ({ name: 'Semantic Scholar', configured: true }),\n ieee: () => ({ name: 'IEEE Xplore', configured: !!config.ieeeApiKey }),\n};\n"}, "4b02d14b7e68a4befea8c319a67dcbfba662f9c5": {"t": 1785754759.592956, "n": 4829, "h": "f1bf60209a0320a9", "c": "import 'dotenv/config';\n\nconst bool = (v: string | undefined): boolean => v === 'true' || v === '1';\n\nconst cleanEnvKey = (v: string | undefined): string => {\n if (!v) return '';\n const trimmed = v.trim();\n if (/^(your-.*|change-this-.*|placeholder)$/i.test(trimmed)) return '';\n return trimmed;\n};\n\nexport const config = {\n env: process.env.NODE_ENV || 'development',\n port: parseInt(process.env.PORT || '5000'),\n frontendUrl: process.env.FRONTEND_URL || 'http://localhost:5173',\n mongoUri: process.env.MONGODB_URI || 'mongodb://localhost:27017/nexus',\n redisUrl: process.env.REDIS_URL || 'redis://localhost:6379',\n redis: {\n host: process.env.REDIS_HOST || 'localhost',\n port: parseInt(process.env.REDIS_PORT || '6379', 10),\n password: process.env.REDIS_PASSWORD || undefined,\n db: parseInt(process.env.REDIS_DB || '0', 10),\n url: process.env.REDIS_URL || 'redis://localhost:6379',\n },\n jwt: {\n // No hardcoded fallbacks — validated at startup by validateConfig().\n secret: process.env.JWT_SECRET || '',\n refreshSecret: process.env.JWT_REFRESH_SECRET || '',\n expiresIn: process.env.JWT_EXPIRES_IN || '15m',\n refreshExpiresIn: process.env.JWT_REFRESH_EXPIRES_IN || '7d',\n },\n geminiApiKey: cleanEnvKey(process.env.GEMINI_API_KEY),\n defaultAiProvider: process.env.DEFAULT_AI_PROVIDER || 'openrouter',\n ai: {\n openrouter: {\n apiKey: cleanEnvKey(process.env.OPENROUTER_API_KEY),\n baseUrl: process.env.OPENROUTER_BASE_URL || 'https://openrouter.ai/api/v1',\n },\n gemini: {\n apiKey: cleanEnvKey(process.env.GEMINI_API_KEY),\n },\n openai: {\n apiKey: cleanEnvKey(process.env.OPENAI_API_KEY),\n },\n anthropic: {\n apiKey: cleanEnvKey(process.env.ANTHROPIC_API_KEY),\n },\n groq: {\n apiKey: cleanEnvKey(process.env.GROQ_API_KEY),\n },\n deepseek: {\n apiKey: cleanEnvKey(process.env.DEEPSEEK_API_KEY),\n },\n together: {\n apiKey: cleanEnvKey(process.env.TOGETHER_API_KEY),\n },\n },\n serperApiKey: cleanEnvKey(process.env.SERPER_API_KEY),\n githubToken: cleanEnvKey(process.env.GITHUB_TOKEN),\n semanticScholarApiKey: cleanEnvKey(process.env.SEMANTIC_SCHOLAR_API_KEY),\n ieeeApiKey: cleanEnvKey(process.env.IEEE_XPLORE_API_KEY),\n chromaUrl: process.env.CHROMA_URL || 'http://localhost:8000',\n rag: {\n chunkSize: parseInt(process.env.RAG_CHUNK_SIZE || '1200'),\n chunkOverlap: parseInt(process.env.RAG_CHUNK_OVERLAP || '150'),\n topK: parseInt(process.env.RAG_TOP_K || '8'),\n maxContextChars: parseInt(process.env.RAG_MAX_CONTEXT_CHARS || '12000'),\n },\n research: {\n workerConcurrency: parseInt(process.env.RESEARCH_WORKER_CONCURRENCY || '2'),\n jobAttempts: parseInt(process.env.RESEARCH_JOB_ATTEMPTS || '2'),\n providerTimeoutMs: parseInt(process.env.PROVIDER_TIMEOUT_MS || '15000'),\n maxSourcesPerProvider: parseInt(process.env.MAX_SOURCES_PER_PROVIDER || '10'),\n },\n copilot: {\n historyWindow: parseInt(process.env.COPILOT_HISTORY_WINDOW || '10'),\n },\n vectorStore: process.env.VECTOR_STORE || 'memory', // 'chroma' | 'memory'\n} as const;\n\n/**\n * Fail-fast validation of required environment. Called once at startup\n * (and by the worker) before anything security-sensitive runs.\n */\nexport const validateConfig = (): void => {\n const missing: string[] = [];\n if (!config.jwt.secret) missing.push('JWT_SECRET');\n if (!config.jwt.refreshSecret) missing.push('JWT_REFRESH_SECRET');\n\n if (missing.length > 0) {\n throw new Error(\n `Missing required environment variables: ${missing.join(', ')}. ` +\n `Set them (see .env.example) before starting the server.`\n );\n }\n\n if (isNaN(config.redis.port) || config.redis.port <= 0 || config.redis.port > 65535) {\n throw new Error(`Invalid REDIS_PORT: \"${process.env.REDIS_PORT}\". Must be a valid port number (1-65535).`);\n }\n\n if (config.redis.url && !/^rediss?:\\/\\//i.test(config.redis.url)) {\n throw new Error(`Invalid REDIS_URL format: \"${config.redis.url}\". Must start with redis:// or rediss://`);\n }\n\n if (config.env === 'production') {\n if (config.jwt.secret.length < 32 || config.jwt.refreshSecret.length < 32) {\n throw new Error('JWT secrets must be at least 32 characters in production.');\n }\n if (config.jwt.secret === config.jwt.refreshSecret) {\n throw new Error('JWT_SECRET and JWT_REFRESH_SECRET must differ.');\n }\n }\n};\n\nexport const providerStatus = {\n gemini: () => ({ name: 'Gemini', configured: !!config.geminiApiKey }),\n serper: () => ({ name: 'Serper', configured: !!config.serperApiKey }),\n github: () => ({ name: 'GitHub', configured: !!config.githubToken }),\n arxiv: () => ({ name: 'arXiv', configured: true }),\n semanticScholar: () => ({ name: 'Semantic Scholar', configured: true }),\n ieee: () => ({ name: 'IEEE Xplore', configured: !!config.ieeeApiKey }),\n};\n"}, "84d72533448a28ba64b6251cdcc4916d272402e7": {"t": 1785754762.8222654, "n": 4416, "h": "fb05addaccd7bdb7", "c": "import 'dotenv/config';\n\nconst bool = (v: string | undefined): boolean => v === 'true' || v === '1';\n\nexport const config = {\n env: process.env.NODE_ENV || 'development',\n port: parseInt(process.env.PORT || '5000'),\n frontendUrl: process.env.FRONTEND_URL || 'http://localhost:5173',\n mongoUri: process.env.MONGODB_URI || 'mongodb://localhost:27017/nexus',\n redisUrl: process.env.REDIS_URL || 'redis://localhost:6379',\n redis: {\n host: process.env.REDIS_HOST || 'localhost',\n port: parseInt(process.env.REDIS_PORT || '6379', 10),\n password: process.env.REDIS_PASSWORD || undefined,\n db: parseInt(process.env.REDIS_DB || '0', 10),\n url: process.env.REDIS_URL || 'redis://localhost:6379',\n },\n jwt: {\n // No hardcoded fallbacks — validated at startup by validateConfig().\n secret: process.env.JWT_SECRET || '',\n refreshSecret: process.env.JWT_REFRESH_SECRET || '',\n expiresIn: process.env.JWT_EXPIRES_IN || '15m',\n refreshExpiresIn: process.env.JWT_REFRESH_EXPIRES_IN || '7d',\n },\n geminiApiKey: process.env.GEMINI_API_KEY || '',\n defaultAiProvider: process.env.DEFAULT_AI_PROVIDER || 'openrouter',\n ai: {\n openrouter: {\n apiKey: process.env.OPENROUTER_API_KEY || '',\n baseUrl: process.env.OPENROUTER_BASE_URL || 'https://openrouter.ai/api/v1',\n },\n gemini: {\n apiKey: process.env.GEMINI_API_KEY || '',\n },\n openai: {\n apiKey: process.env.OPENAI_API_KEY || '',\n },\n anthropic: {\n apiKey: process.env.ANTHROPIC_API_KEY || '',\n },\n groq: {\n apiKey: process.env.GROQ_API_KEY || '',\n },\n deepseek: {\n apiKey: process.env.DEEPSEEK_API_KEY || '',\n },\n together: {\n apiKey: process.env.TOGETHER_API_KEY || '',\n },\n },\n serperApiKey: process.env.SERPER_API_KEY || '',\n githubToken: process.env.GITHUB_TOKEN || '',\n semanticScholarApiKey: process.env.SEMANTIC_SCHOLAR_API_KEY || '',\n chromaUrl: process.env.CHROMA_URL || 'http://localhost:8000',\n rag: {\n chunkSize: parseInt(process.env.RAG_CHUNK_SIZE || '1200'),\n chunkOverlap: parseInt(process.env.RAG_CHUNK_OVERLAP || '150'),\n topK: parseInt(process.env.RAG_TOP_K || '8'),\n maxContextChars: parseInt(process.env.RAG_MAX_CONTEXT_CHARS || '12000'),\n },\n research: {\n workerConcurrency: parseInt(process.env.RESEARCH_WORKER_CONCURRENCY || '2'),\n jobAttempts: parseInt(process.env.RESEARCH_JOB_ATTEMPTS || '2'),\n providerTimeoutMs: parseInt(process.env.PROVIDER_TIMEOUT_MS || '15000'),\n maxSourcesPerProvider: parseInt(process.env.MAX_SOURCES_PER_PROVIDER || '10'),\n },\n copilot: {\n historyWindow: parseInt(process.env.COPILOT_HISTORY_WINDOW || '10'),\n },\n vectorStore: process.env.VECTOR_STORE || 'memory', // 'chroma' | 'memory'\n} as const;\n\n/**\n * Fail-fast validation of required environment. Called once at startup\n * (and by the worker) before anything security-sensitive runs.\n */\nexport const validateConfig = (): void => {\n const missing: string[] = [];\n if (!config.jwt.secret) missing.push('JWT_SECRET');\n if (!config.jwt.refreshSecret) missing.push('JWT_REFRESH_SECRET');\n\n if (missing.length > 0) {\n throw new Error(\n `Missing required environment variables: ${missing.join(', ')}. ` +\n `Set them (see .env.example) before starting the server.`\n );\n }\n\n if (isNaN(config.redis.port) || config.redis.port <= 0 || config.redis.port > 65535) {\n throw new Error(`Invalid REDIS_PORT: \"${process.env.REDIS_PORT}\". Must be a valid port number (1-65535).`);\n }\n\n if (config.redis.url && !/^rediss?:\\/\\//i.test(config.redis.url)) {\n throw new Error(`Invalid REDIS_URL format: \"${config.redis.url}\". Must start with redis:// or rediss://`);\n }\n\n if (config.env === 'production') {\n if (config.jwt.secret.length < 32 || config.jwt.refreshSecret.length < 32) {\n throw new Error('JWT secrets must be at least 32 characters in production.');\n }\n if (config.jwt.secret === config.jwt.refreshSecret) {\n throw new Error('JWT_SECRET and JWT_REFRESH_SECRET must differ.');\n }\n }\n};\n\nexport const providerStatus = {\n gemini: () => ({ name: 'Gemini', configured: !!config.geminiApiKey }),\n serper: () => ({ name: 'Serper', configured: !!config.serperApiKey }),\n github: () => ({ name: 'GitHub', configured: !!config.githubToken }),\n arxiv: () => ({ name: 'arXiv', configured: true }),\n semanticScholar: () => ({ name: 'Semantic Scholar', configured: true }),\n};\n"}, "ddfa2a549fb070320082c4e5a3e54fe4157c9663": {"t": 1785754764.815954, "n": 3069, "h": "50f031874a09804d", "c": "import 'dotenv/config';\n\nconst bool = (v: string | undefined): boolean => v === 'true' || v === '1';\n\nexport const config = {\n env: process.env.NODE_ENV || 'development',\n port: parseInt(process.env.PORT || '5000'),\n frontendUrl: process.env.FRONTEND_URL || 'http://localhost:5173',\n mongoUri: process.env.MONGODB_URI || 'mongodb://localhost:27017/nexus',\n redisUrl: process.env.REDIS_URL || 'redis://localhost:6379',\n jwt: {\n // No hardcoded fallbacks — validated at startup by validateConfig().\n secret: process.env.JWT_SECRET || '',\n refreshSecret: process.env.JWT_REFRESH_SECRET || '',\n expiresIn: process.env.JWT_EXPIRES_IN || '15m',\n refreshExpiresIn: process.env.JWT_REFRESH_EXPIRES_IN || '7d',\n },\n geminiApiKey: process.env.GEMINI_API_KEY || '',\n serperApiKey: process.env.SERPER_API_KEY || '',\n githubToken: process.env.GITHUB_TOKEN || '',\n semanticScholarApiKey: process.env.SEMANTIC_SCHOLAR_API_KEY || '',\n chromaUrl: process.env.CHROMA_URL || 'http://localhost:8000',\n rag: {\n chunkSize: parseInt(process.env.RAG_CHUNK_SIZE || '1200'),\n chunkOverlap: parseInt(process.env.RAG_CHUNK_OVERLAP || '150'),\n topK: parseInt(process.env.RAG_TOP_K || '8'),\n maxContextChars: parseInt(process.env.RAG_MAX_CONTEXT_CHARS || '12000'),\n },\n research: {\n workerConcurrency: parseInt(process.env.RESEARCH_WORKER_CONCURRENCY || '2'),\n jobAttempts: parseInt(process.env.RESEARCH_JOB_ATTEMPTS || '2'),\n providerTimeoutMs: parseInt(process.env.PROVIDER_TIMEOUT_MS || '15000'),\n maxSourcesPerProvider: parseInt(process.env.MAX_SOURCES_PER_PROVIDER || '10'),\n },\n copilot: {\n historyWindow: parseInt(process.env.COPILOT_HISTORY_WINDOW || '10'),\n },\n vectorStore: process.env.VECTOR_STORE || 'memory', // 'chroma' | 'memory'\n} as const;\n\n/**\n * Fail-fast validation of required environment. Called once at startup\n * (and by the worker) before anything security-sensitive runs.\n */\nexport const validateConfig = (): void => {\n const missing: string[] = [];\n if (!config.jwt.secret) missing.push('JWT_SECRET');\n if (!config.jwt.refreshSecret) missing.push('JWT_REFRESH_SECRET');\n\n if (missing.length > 0) {\n throw new Error(\n `Missing required environment variables: ${missing.join(', ')}. ` +\n `Set them (see .env.example) before starting the server.`\n );\n }\n\n if (config.env === 'production') {\n if (config.jwt.secret.length < 32 || config.jwt.refreshSecret.length < 32) {\n throw new Error('JWT secrets must be at least 32 characters in production.');\n }\n if (config.jwt.secret === config.jwt.refreshSecret) {\n throw new Error('JWT_SECRET and JWT_REFRESH_SECRET must differ.');\n }\n }\n};\n\nexport const providerStatus = {\n gemini: () => ({ name: 'Gemini', configured: !!config.geminiApiKey }),\n serper: () => ({ name: 'Serper', configured: !!config.serperApiKey }),\n github: () => ({ name: 'GitHub', configured: !!config.githubToken }),\n arxiv: () => ({ name: 'arXiv', configured: true }),\n semanticScholar: () => ({ name: 'Semantic Scholar', configured: true }),\n};\n"}}}} |