Skip to main content
Glama
zqushair
by zqushair
configReloader.ts7.67 kB
/** * Configuration reloader * This utility provides functions for reloading configuration at runtime */ import fs from 'fs'; import path from 'path'; import dotenv from 'dotenv'; import { validateEnvVars } from './envValidator.js'; import { envVarDefinitions } from '../config/schema.js'; import { config } from '../config/index.js'; import logger from './logger.js'; /** * Configuration reload options */ export interface ConfigReloadOptions { /** Whether to watch the .env file for changes */ watchEnvFile: boolean; /** The path to the .env file */ envFilePath: string; /** The interval in milliseconds to check for changes */ watchInterval: number; /** Callback function to run after reloading configuration */ onReload?: (newConfig: typeof config) => void; } /** * Default configuration reload options */ const DEFAULT_CONFIG_RELOAD_OPTIONS: ConfigReloadOptions = { watchEnvFile: true, envFilePath: '.env', watchInterval: 10000, // 10 seconds }; /** * Configuration reloader * This class provides functionality for reloading configuration at runtime */ export class ConfigReloader { public options: ConfigReloadOptions; private envFileLastModified: number = 0; private watchIntervalId: NodeJS.Timeout | null = null; private isWatching: boolean = false; /** * Create a new configuration reloader * @param options Configuration reload options */ constructor(options: Partial<ConfigReloadOptions> = {}) { this.options = { ...DEFAULT_CONFIG_RELOAD_OPTIONS, ...options, }; } /** * Start watching for configuration changes */ public startWatching(): void { if (this.isWatching) { logger.warn('Configuration reloader is already watching for changes'); return; } this.isWatching = true; // Check if the .env file exists if (this.options.watchEnvFile) { try { const stats = fs.statSync(this.options.envFilePath); this.envFileLastModified = stats.mtimeMs; logger.info(`Watching .env file for changes: ${this.options.envFilePath}`); } catch (error) { logger.warn(`Could not access .env file: ${this.options.envFilePath}`, { error: error instanceof Error ? error.message : String(error), }); } } // Start the watch interval this.watchIntervalId = setInterval(() => { this.checkForChanges(); }, this.options.watchInterval); logger.info('Configuration reloader started', { watchInterval: this.options.watchInterval, watchEnvFile: this.options.watchEnvFile, }); } /** * Stop watching for configuration changes */ public stopWatching(): void { if (!this.isWatching) { logger.warn('Configuration reloader is not watching for changes'); return; } if (this.watchIntervalId) { clearInterval(this.watchIntervalId); this.watchIntervalId = null; } this.isWatching = false; logger.info('Configuration reloader stopped'); } /** * Check for configuration changes */ private checkForChanges(): void { if (this.options.watchEnvFile) { try { const stats = fs.statSync(this.options.envFilePath); const lastModified = stats.mtimeMs; // If the file has been modified, reload the configuration if (lastModified > this.envFileLastModified) { logger.info(`Detected changes in .env file, reloading configuration`); this.envFileLastModified = lastModified; this.reloadConfig(); } } catch (error) { logger.warn(`Could not check .env file for changes: ${this.options.envFilePath}`, { error: error instanceof Error ? error.message : String(error), }); } } } /** * Reload configuration * @returns The new configuration */ public reloadConfig(): typeof config { try { // Reload environment variables from .env file dotenv.config({ path: this.options.envFilePath, override: true }); // Validate environment variables const validatedEnv = validateEnvVars(envVarDefinitions); // Update configuration this.updateConfig(validatedEnv); logger.info('Configuration reloaded successfully'); // Call the onReload callback if provided if (this.options.onReload) { this.options.onReload(config); } return config; } catch (error) { logger.error('Failed to reload configuration', { error: error instanceof Error ? error.message : String(error), }); throw error; } } /** * Update configuration with new values * @param validatedEnv Validated environment variables */ private updateConfig(validatedEnv: Record<string, any>): void { // Update Frontapp configuration config.frontapp.apiKey = validatedEnv.FRONTAPP_API_KEY || config.frontapp.apiKey; // Update webhook configuration config.webhook.secret = validatedEnv.WEBHOOK_SECRET || config.webhook.secret; config.webhook.baseUrl = validatedEnv.WEBHOOK_BASE_URL || config.webhook.baseUrl; // Update server configuration config.server.port = validatedEnv.PORT || config.server.port; // Update logging configuration config.logging.level = validatedEnv.LOG_LEVEL || config.logging.level; config.logging.metricsInterval = validatedEnv.METRICS_INTERVAL || config.logging.metricsInterval; // Update API configuration config.api.apiKey = validatedEnv.API_KEY || config.api.apiKey; config.api.corsOrigins = validatedEnv.CORS_ORIGINS || config.api.corsOrigins; config.api.rateLimitWindowMs = validatedEnv.RATE_LIMIT_WINDOW_MS || config.api.rateLimitWindowMs; config.api.rateLimitMax = validatedEnv.RATE_LIMIT_MAX || config.api.rateLimitMax; // Update security configuration config.security.encryptionKey = validatedEnv.ENCRYPTION_KEY || config.security.encryptionKey; config.security.credentialsDir = validatedEnv.CREDENTIALS_DIR || config.security.credentialsDir; // Update HTTPS configuration if (validatedEnv.HTTPS_ENABLED !== undefined) { config.security.https.enabled = validatedEnv.HTTPS_ENABLED; } config.security.https.cert = validatedEnv.HTTPS_CERT || config.security.https.cert; config.security.https.key = validatedEnv.HTTPS_KEY || config.security.https.key; // Update rate limiting configuration if (validatedEnv.RATE_LIMITING_ENABLED !== undefined) { config.security.rateLimiting.enabled = validatedEnv.RATE_LIMITING_ENABLED; } config.security.rateLimiting.windowMs = validatedEnv.RATE_LIMIT_WINDOW_MS || config.security.rateLimiting.windowMs; config.security.rateLimiting.max = validatedEnv.RATE_LIMIT_MAX || config.security.rateLimiting.max; config.security.rateLimiting.api.windowMs = validatedEnv.API_RATE_LIMIT_WINDOW_MS || config.security.rateLimiting.api.windowMs; config.security.rateLimiting.api.max = validatedEnv.API_RATE_LIMIT_MAX || config.security.rateLimiting.api.max; config.security.rateLimiting.auth.windowMs = validatedEnv.AUTH_RATE_LIMIT_WINDOW_MS || config.security.rateLimiting.auth.windowMs; config.security.rateLimiting.auth.max = validatedEnv.AUTH_RATE_LIMIT_MAX || config.security.rateLimiting.auth.max; config.security.rateLimiting.webhook.windowMs = validatedEnv.WEBHOOK_RATE_LIMIT_WINDOW_MS || config.security.rateLimiting.webhook.windowMs; config.security.rateLimiting.webhook.max = validatedEnv.WEBHOOK_RATE_LIMIT_MAX || config.security.rateLimiting.webhook.max; } } // Export a singleton instance export const configReloader = new ConfigReloader(); // Export default export default configReloader;

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/zqushair/Frontapp-MCP'

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