Skip to main content
Glama
index.ts4.8 kB
import dotenv from 'dotenv'; import path from 'path'; import fs from 'fs-extra'; import { SecurityManager } from './security.js'; // Load environment variables dotenv.config(); /** * Configuration management with fail-safe defaults * Following WCGW: Assume config can be missing, malformed, or malicious */ export class Config { private static instance: Config; private config: Map<string, any> = new Map(); private encryptionKey: string; private constructor() { this.encryptionKey = process.env.CONFIG_ENCRYPTION_KEY || SecurityManager.generateSecureToken(32); this.loadConfiguration(); } static getInstance(): Config { if (!Config.instance) { Config.instance = new Config(); } return Config.instance; } private loadConfiguration(): void { // Load from environment with validation this.config.set('NODE_ENV', process.env.NODE_ENV || 'production'); this.config.set('PORT', this.validatePort(process.env.PORT)); this.config.set('LOG_LEVEL', process.env.LOG_LEVEL || 'info'); // Security settings this.config.set('JWT_SECRET', process.env.JWT_SECRET || SecurityManager.generateSecureToken(64)); this.config.set('SESSION_SECRET', process.env.SESSION_SECRET || SecurityManager.generateSecureToken(64)); // Database settings (encrypted) if (process.env.DATABASE_URL) { this.config.set('DATABASE_URL', SecurityManager.encrypt(process.env.DATABASE_URL, this.encryptionKey)); } // Rate limiting this.config.set('RATE_LIMIT_WINDOW_MS', parseInt(process.env.RATE_LIMIT_WINDOW_MS || '60000', 10)); this.config.set('RATE_LIMIT_MAX_REQUESTS', parseInt(process.env.RATE_LIMIT_MAX_REQUESTS || '100', 10)); // Resource limits this.config.set('MAX_FILE_SIZE', parseInt(process.env.MAX_FILE_SIZE || '10485760', 10)); // 10MB this.config.set('MAX_MEMORY_MB', parseInt(process.env.MAX_MEMORY_MB || '512', 10)); this.config.set('COMMAND_TIMEOUT_MS', parseInt(process.env.COMMAND_TIMEOUT_MS || '30000', 10)); // Paths this.config.set('WORKSPACE_ROOT', process.env.WORKSPACE_ROOT || path.join(process.env.HOME || '', 'mcp-workspace')); this.config.set('LOG_DIR', process.env.LOG_DIR || path.join(process.cwd(), 'logs')); this.config.set('TEMP_DIR', process.env.TEMP_DIR || path.join(process.cwd(), 'temp')); } private validatePort(port?: string): number { const parsed = parseInt(port || '3000', 10); if (isNaN(parsed) || parsed < 1 || parsed > 65535) { return 3000; } return parsed; } get<T>(key: string): T { if (!this.config.has(key)) { throw new Error(`Configuration key '${key}' not found`); } const value = this.config.get(key); // Decrypt if it's a sensitive value if (key.includes('DATABASE_URL') && typeof value === 'string') { return SecurityManager.decrypt(value, this.encryptionKey) as T; } return value as T; } getString(key: string, defaultValue?: string): string { try { return this.get<string>(key); } catch { return defaultValue || ''; } } getNumber(key: string, defaultValue?: number): number { try { return this.get<number>(key); } catch { return defaultValue || 0; } } getBoolean(key: string, defaultValue?: boolean): boolean { try { const value = this.get<string | boolean>(key); if (typeof value === 'boolean') return value; return value === 'true' || value === '1'; } catch { return defaultValue || false; } } /** * Validate all required configurations exist */ validate(): void { const required = [ 'NODE_ENV', 'JWT_SECRET', 'SESSION_SECRET', 'WORKSPACE_ROOT' ]; const missing = required.filter(key => !this.config.has(key)); if (missing.length > 0) { throw new Error(`Missing required configuration: ${missing.join(', ')}`); } } /** * Get safe configuration for logging (no secrets) */ getSafeConfig(): Record<string, any> { const safe: Record<string, any> = {}; const secrets = ['JWT_SECRET', 'SESSION_SECRET', 'DATABASE_URL', 'API_KEY', 'ENCRYPTION_KEY']; for (const [key, value] of this.config.entries()) { if (secrets.some(secret => key.includes(secret))) { safe[key] = '[REDACTED]'; } else { safe[key] = value; } } return safe; } } // Export singleton instance export const config = Config.getInstance(); // Validate on startup try { config.validate(); } catch (error) { console.error('Configuration validation failed:', error); process.exit(1); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Rajawatrajat/mcp-software-engineer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server