UNPKG

mcp-quiz-server

Version:

🧠 AI-Powered Quiz Management via Model Context Protocol (MCP) - Create, manage, and take quizzes directly from VS Code, Claude, and other AI agents.

369 lines (368 loc) 14.9 kB
"use strict"; /** * @moduleName: MVP Configuration (Local Only) * @version: 1.0.0 * @since: 2025-07-24 * @lastUpdated: 2025-07-27 * @projectSummary: Simple configuration for local-only MVP deployment without authentication * @techStack: TypeScript, Environment Variables, Feature Flags * @dependency: None (no external services) * @interModuleDependency: Used by all services to determine feature availability * @requirementsTraceability: * {@link Requirements.REQ_CONFIG_004} (Feature Flag Management) * {@link Requirements.REQ_CONFIG_005} (Environment Configuration) * {@link Requirements.REQ_ARCH_003} (MVP Architecture) * @briefDescription: Configuration management for MVP with feature flags and environment overrides * @methods: getConfig, isFeatureEnabled, isDevelopment, isProduction * @contributors: GitHub Copilot, Open Source Community * @examples: if (Config.isFeatureEnabled('USER_AUTH')) { ... } * @vulnerabilitiesAssessment: No secrets stored, local configuration only, safe defaults */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Config = void 0; exports.isFeatureEnabled = isFeatureEnabled; exports.isDev = isDev; exports.devLog = devLog; /** * @description Parse boolean from environment variable with fallback */ function getEnvBoolean(key, defaultValue) { const value = process.env[key]; if (value === undefined) return defaultValue; return value.toLowerCase() === 'true'; } /** * @description Detect if running in OSS mode (NPX users, default mode) */ function isOSSMode() { // Explicitly set OSS mode if (process.env.OSS_MODE === 'true') return true; // Auto-detect OSS mode indicators if (process.env.OSS_MODE === 'false') return false; // Default to OSS mode for NPX users and most scenarios // Only disable OSS mode if explicitly configured for enterprise return !getEnvBoolean('ENTERPRISE_MODE', false); } /** * @description Parse deployment metadata from environment */ function getDeploymentMetadata() { return { buildId: process.env.BUILD_ID || process.env.GITHUB_RUN_ID || 'unknown', deploymentId: process.env.DEPLOYMENT_ID || `deploy-${Date.now()}`, gitCommit: process.env.GIT_COMMIT || process.env.GITHUB_SHA || 'unknown', gitBranch: process.env.GIT_BRANCH || process.env.GITHUB_REF_NAME || 'unknown', timestamp: new Date().toISOString(), }; } /** * @description Default configuration for MVP with environment override support */ const DEFAULT_CONFIG = { environment: process.env.NODE_ENV || 'development', port: parseInt(process.env.PORT || '3001'), host: process.env.HOST || 'localhost', database: { type: 'sqlite', path: process.env.DATABASE_PATH || process.env.DB_PATH || './data/quiz.db', synchronize: getEnvBoolean('DATABASE_SYNCHRONIZE', process.env.NODE_ENV !== 'production'), logging: getEnvBoolean('DATABASE_LOGGING', process.env.NODE_ENV === 'development'), }, features: { // Environment-overridable feature flags with FEATURE_ prefix // OSS-friendly defaults: premium features disabled by default userAuth: getEnvBoolean('FEATURE_USER_AUTH', !isOSSMode()), // DISABLED for OSS, enabled for enterprise cloudSync: getEnvBoolean('FEATURE_CLOUD_SYNC', false), multiUser: getEnvBoolean('FEATURE_MULTI_USER', false), analytics: getEnvBoolean('FEATURE_ANALYTICS', false), realtime: getEnvBoolean('FEATURE_REALTIME', false), // MVP core features (default enabled) localQuizzes: getEnvBoolean('FEATURE_LOCAL_QUIZZES', true), timerFeature: getEnvBoolean('FEATURE_TIMER_FEATURE', true), themeToggle: getEnvBoolean('FEATURE_THEME_TOGGLE', true), mcpProtocol: getEnvBoolean('FEATURE_MCP_PROTOCOL', true), settingsPersistence: getEnvBoolean('FEATURE_SETTINGS_PERSISTENCE', true), // Development features debugMode: getEnvBoolean('FEATURE_DEBUG_MODE', process.env.NODE_ENV === 'development'), testData: getEnvBoolean('FEATURE_TEST_DATA', process.env.NODE_ENV !== 'production'), // CI/CD and staging features advancedLogging: getEnvBoolean('FEATURE_ADVANCED_LOGGING', false), performanceMonitoring: getEnvBoolean('FEATURE_PERFORMANCE_MONITORING', process.env.NODE_ENV === 'staging'), // Enhanced Routes Feature Flag enhancedRoutes: getEnvBoolean('ENHANCED_ROUTES_ENABLED', true), // SSE Transport Feature Flag sseTransport: getEnvBoolean('FEATURE_SSE_TRANSPORT', false), }, // SSE Transport Configuration (Premium feature, disabled by default for OSS) sseTransport: { enabled: getEnvBoolean('SSE_TRANSPORT_ENABLED', false), port: parseInt(process.env.SSE_PORT || '3001'), host: process.env.SSE_HOST || 'localhost', enableAuthentication: getEnvBoolean('SSE_ENABLE_AUTH', !isOSSMode()), // Disabled for OSS corsOrigins: (process.env.SSE_CORS_ORIGINS || 'http://localhost:3000,http://127.0.0.1:3000').split(','), connectionTimeout: parseInt(process.env.SSE_CONNECTION_TIMEOUT || '300000'), maxConnections: parseInt(process.env.SSE_MAX_CONNECTIONS || '100'), heartbeatInterval: parseInt(process.env.SSE_HEARTBEAT_INTERVAL || '30000'), }, deployment: getDeploymentMetadata(), ui: { defaultTheme: 'auto', enableAnimations: true, compactMode: false, }, storage: { settingsKey: 'quiz-app-settings', maxStorageSize: 1024 * 1024, // 1MB max for settings }, }; /** * @description Configuration management service */ class Config { /** * @description Gets the current configuration * @returns {AppConfig} Current application configuration */ static getConfig() { return { ...this.config }; } /** * @description Checks if a feature is enabled * @param {keyof AppConfig['features']} feature Feature name to check * @returns {boolean} True if feature is enabled */ static isFeatureEnabled(feature) { return this.config.features[feature]; } /** * @description Checks if running in development mode * @returns {boolean} True if development environment */ static isDevelopment() { return this.config.environment === 'development'; } static isProduction() { return this.config.environment === 'production'; } /** * @description Checks if running in test mode * @returns {boolean} True if test environment */ static isTest() { return this.config.environment === 'test'; } /** * @description Gets database configuration * @returns {AppConfig['database']} Database settings */ static getDatabaseConfig() { return { ...this.config.database }; } /** * @description Gets server configuration * @returns {object} Server host and port */ static getServerConfig() { return { host: this.config.host, port: this.config.port, }; } /** * @description Gets UI configuration * @returns {AppConfig['ui']} UI settings */ static getUIConfig() { return { ...this.config.ui }; } /** * @description Updates configuration (for testing or runtime changes) * @param {Partial<AppConfig>} updates Configuration updates */ static updateConfig(updates) { this.config = { ...this.config, ...updates, features: { ...this.config.features, ...(updates.features || {}), }, }; } /** * @description Resets configuration to defaults */ static resetToDefaults() { this.config = { ...DEFAULT_CONFIG }; } /** * @description Checks if running in OSS mode * @returns {boolean} True if OSS mode (NPX users, default) */ static isOSSMode() { return isOSSMode(); } /** * @description Gets OSS-friendly configuration summary for logging * @returns {string} Configuration summary */ static getModeSummary() { const mode = isOSSMode() ? 'OSS (Open Source)' : 'Enterprise'; const authStatus = this.config.features.userAuth ? 'ON' : 'OFF'; const sseStatus = this.config.features.sseTransport ? 'ON' : 'OFF'; return `🎯 Mode: ${mode} | Auth: ${authStatus} | SSE: ${sseStatus} | Local Quizzes: UNLIMITED`; } /** * @description Gets feature flags for frontend * @returns {object} Feature flags safe for client-side */ static getClientFeatures() { return { localQuizzes: this.config.features.localQuizzes, timerFeature: this.config.features.timerFeature, themeToggle: this.config.features.themeToggle, settingsPersistence: this.config.features.settingsPersistence, debugMode: this.config.features.debugMode, }; } /** * @description Gets deployment metadata * @returns {AppConfig['deployment']} Deployment information */ static getDeploymentInfo() { return { ...this.config.deployment }; } /** * @description Checks if running in staging mode * @returns {boolean} True if staging environment */ static isStaging() { return this.config.environment === 'staging'; } /** * @description Gets environment-specific feature overrides * @returns {object} Environment feature configuration */ static getEnvironmentFeatures() { const env = this.config.environment; const overrides = {}; // Environment-specific feature configurations Object.keys(process.env).forEach(key => { var _a; if (key.startsWith('FEATURE_')) { const featureName = key.replace('FEATURE_', '').toLowerCase(); const camelCaseName = featureName.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()); overrides[camelCaseName] = ((_a = process.env[key]) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'true'; } }); return overrides; } /** * @description Logs current feature flag configuration */ static logFeatureFlags() { const features = this.config.features; const enabledFeatures = Object.entries(features) .filter(([_, enabled]) => enabled) .map(([name]) => name); const disabledFeatures = Object.entries(features) .filter(([_, enabled]) => !enabled) .map(([name]) => name); console.log(`🎛️ Feature Flags Configuration (${this.config.environment}):`); console.log(` ✅ Enabled: ${enabledFeatures.join(', ')}`); console.log(` ❌ Disabled: ${disabledFeatures.join(', ')}`); if (this.config.features.debugMode) { console.log(` 🔍 Debug mode active - additional logging enabled`); } } /** * @description Validates configuration on startup with enhanced CI/CD checks * @throws {Error} If configuration is invalid */ static validateConfig() { const config = this.getConfig(); // Validate port range if (config.port < 1 || config.port > 65535) { throw new Error(`Invalid port: ${config.port}. Must be between 1 and 65535.`); } // Validate environment if (!['development', 'production', 'test', 'staging'].includes(config.environment)) { throw new Error(`Invalid environment: ${config.environment}`); } // Environment-specific validation if (config.environment === 'production') { // Ensure MVP safety - no dangerous features enabled in production const unsafeFeatures = []; // NOTE: userAuth is SAFE for production - it's fully implemented security if (config.features.cloudSync) unsafeFeatures.push('cloudSync'); if (config.features.multiUser) unsafeFeatures.push('multiUser'); if (config.features.analytics) unsafeFeatures.push('analytics'); if (config.features.realtime) unsafeFeatures.push('realtime'); if (unsafeFeatures.length > 0) { throw new Error(`❌ Production deployment blocked: Unsafe features enabled: ${unsafeFeatures.join(', ')}. ` + 'These features should be disabled for MVP production deployment.'); } // Validate authentication is enabled for production security if (!config.features.userAuth) { console.warn('⚠️ SECURITY WARNING: Authentication is disabled in production. ' + 'Set FEATURE_USER_AUTH=true for production security.'); } // Verify core MVP features are enabled const requiredFeatures = [ 'localQuizzes', 'timerFeature', 'mcpProtocol', ]; const missingFeatures = requiredFeatures.filter(feature => !config.features[feature]); if (missingFeatures.length > 0) { throw new Error(`❌ Production deployment blocked: Required MVP features disabled: ${missingFeatures.join(', ')}`); } } // Staging environment validation if (config.environment === 'staging') { console.log(`🚀 Staging environment detected - experimental features may be enabled`); } // Log feature flag configuration this.logFeatureFlags(); console.log(`✅ Configuration validated for ${config.environment} environment`); console.log(`📦 Build: ${config.deployment.buildId} | Commit: ${config.deployment.gitCommit.substring(0, 8)}`); } } exports.Config = Config; Config.config = { ...DEFAULT_CONFIG }; // Initialize and validate configuration on module load Config.validateConfig(); // Export default configuration exports.default = Config; /** * @description Helper to get feature flag in a type-safe way * @param {keyof AppConfig['features']} feature Feature to check * @returns {boolean} Whether feature is enabled */ function isFeatureEnabled(feature) { return Config.isFeatureEnabled(feature); } /** * @description Helper to check if we're in development mode * @returns {boolean} True if development */ function isDev() { return Config.isDevelopment(); } /** * @description Environment-specific console logging * @param {string} message Message to log * @param {any} data Optional data to log */ function devLog(message, ...data) { if (Config.isDevelopment()) { console.log(`[DEV] ${message}`, ...data); } }