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
JavaScript
;
/**
* @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);
}
}