This commit is contained in:
nasir@endelospay.com
2025-07-11 20:22:12 +05:00
commit 8c74b0e23f
120 changed files with 206874 additions and 0 deletions

438
src/config/ConfigManager.js Normal file
View File

@@ -0,0 +1,438 @@
/**
* @fileoverview Configuration Manager for Laravel Healthcare MCP Server
* Handles environment variables, validation, and configuration loading
*/
import dotenv from "dotenv";
import { logger } from "../utils/logger.js";
import { ConfigurationError } from "../utils/errors.js";
/**
* Configuration Manager class
* Manages application configuration and environment variables
*/
export class ConfigManager {
/**
* Create ConfigManager instance
* @param {string} envPath - Path to .env file (optional)
*/
constructor(envPath = null) {
this.config = {};
this.validationRules = this._defineValidationRules();
// Load environment variables
this._loadEnvironment(envPath);
// Load and validate configuration
this._loadConfiguration();
this._validateConfiguration();
logger.info("Configuration loaded and validated successfully");
}
/**
* Load environment variables from .env file
* @private
* @param {string} envPath - Path to .env file
*/
_loadEnvironment(envPath) {
try {
const result = dotenv.config({ path: envPath });
if (result.error && envPath) {
logger.warn(
`Failed to load .env file from ${envPath}:`,
result.error.message
);
}
logger.debug("Environment variables loaded");
} catch (error) {
logger.warn("Error loading environment variables:", error.message);
}
}
/**
* Load configuration from environment variables
* @private
*/
_loadConfiguration() {
// Laravel API Configuration
this.config.LARAVEL_API_BASE_URL = process.env.LARAVEL_API_BASE_URL;
this.config.LARAVEL_API_TIMEOUT =
parseInt(process.env.LARAVEL_API_TIMEOUT) || 30000;
this.config.LARAVEL_API_RETRY_ATTEMPTS =
parseInt(process.env.LARAVEL_API_RETRY_ATTEMPTS) || 3;
this.config.LARAVEL_API_RETRY_DELAY =
parseInt(process.env.LARAVEL_API_RETRY_DELAY) || 1000;
// MCP Server Configuration
this.config.MCP_SERVER_NAME =
process.env.MCP_SERVER_NAME || "laravel-healthcare-mcp-server";
this.config.MCP_SERVER_VERSION = process.env.MCP_SERVER_VERSION || "1.0.0";
this.config.MCP_SERVER_PORT = parseInt(process.env.MCP_SERVER_PORT) || 3000;
// Authentication Configuration
this.config.ADMIN_USERNAME = process.env.ADMIN_USERNAME;
this.config.ADMIN_PASSWORD = process.env.ADMIN_PASSWORD;
this.config.ADMIN_LOGIN_ENDPOINT =
process.env.ADMIN_LOGIN_ENDPOINT || "/api/admin/login";
this.config.ADMIN_TOKEN_REFRESH_THRESHOLD =
parseInt(process.env.ADMIN_TOKEN_REFRESH_THRESHOLD) || 300;
this.config.AGENT_USERNAME = process.env.AGENT_USERNAME;
this.config.AGENT_PASSWORD = process.env.AGENT_PASSWORD;
this.config.AGENT_LOGIN_ENDPOINT =
process.env.AGENT_LOGIN_ENDPOINT || "/agent/login/post";
this.config.AGENT_TOKEN_REFRESH_THRESHOLD =
parseInt(process.env.AGENT_TOKEN_REFRESH_THRESHOLD) || 300;
this.config.PATIENT_USERNAME = process.env.PATIENT_USERNAME;
this.config.PATIENT_PASSWORD = process.env.PATIENT_PASSWORD;
this.config.PATIENT_LOGIN_ENDPOINT =
process.env.PATIENT_LOGIN_ENDPOINT || "/api/frontend/login";
this.config.PATIENT_TOKEN_REFRESH_THRESHOLD =
parseInt(process.env.PATIENT_TOKEN_REFRESH_THRESHOLD) || 300;
this.config.PRACTITIONER_USERNAME = process.env.PRACTITIONER_USERNAME;
this.config.PRACTITIONER_PASSWORD = process.env.PRACTITIONER_PASSWORD;
this.config.PRACTITIONER_LOGIN_ENDPOINT =
process.env.PRACTITIONER_LOGIN_ENDPOINT || "/api/practitioner/login";
this.config.PRACTITIONER_TOKEN_REFRESH_THRESHOLD =
parseInt(process.env.PRACTITIONER_TOKEN_REFRESH_THRESHOLD) || 300;
this.config.AFFILIATE_USERNAME = process.env.AFFILIATE_USERNAME;
this.config.AFFILIATE_PASSWORD = process.env.AFFILIATE_PASSWORD;
this.config.AFFILIATE_LOGIN_ENDPOINT =
process.env.AFFILIATE_LOGIN_ENDPOINT || "/api/affiliate/login";
this.config.AFFILIATE_TOKEN_REFRESH_THRESHOLD =
parseInt(process.env.AFFILIATE_TOKEN_REFRESH_THRESHOLD) || 300;
this.config.PARTNER_USERNAME = process.env.PARTNER_USERNAME;
this.config.PARTNER_PASSWORD = process.env.PARTNER_PASSWORD;
this.config.PARTNER_LOGIN_ENDPOINT =
process.env.PARTNER_LOGIN_ENDPOINT || "/api/partner/login";
this.config.PARTNER_TOKEN_REFRESH_THRESHOLD =
parseInt(process.env.PARTNER_TOKEN_REFRESH_THRESHOLD) || 300;
this.config.NETWORK_USERNAME = process.env.NETWORK_USERNAME;
this.config.NETWORK_PASSWORD = process.env.NETWORK_PASSWORD;
this.config.NETWORK_LOGIN_ENDPOINT =
process.env.NETWORK_LOGIN_ENDPOINT || "/api/network/login";
this.config.NETWORK_TOKEN_REFRESH_THRESHOLD =
parseInt(process.env.NETWORK_TOKEN_REFRESH_THRESHOLD) || 300;
this.config.DOCTOR_USERNAME = process.env.DOCTOR_USERNAME;
this.config.DOCTOR_PASSWORD = process.env.DOCTOR_PASSWORD;
this.config.DOCTOR_LOGIN_ENDPOINT =
process.env.DOCTOR_LOGIN_ENDPOINT || "/api/doctor/login";
this.config.DOCTOR_TOKEN_REFRESH_THRESHOLD =
parseInt(process.env.DOCTOR_TOKEN_REFRESH_THRESHOLD) || 300;
this.config.PROVIDER_USERNAME = process.env.PROVIDER_USERNAME;
this.config.PROVIDER_PASSWORD = process.env.PROVIDER_PASSWORD;
this.config.PROVIDER_LOGIN_ENDPOINT =
process.env.PROVIDER_LOGIN_ENDPOINT || "/api/provider/login";
this.config.PROVIDER_TOKEN_REFRESH_THRESHOLD =
parseInt(process.env.PROVIDER_TOKEN_REFRESH_THRESHOLD) || 300;
// Token Management
this.config.TOKEN_CACHE_DURATION =
parseInt(process.env.TOKEN_CACHE_DURATION) || 3600;
this.config.TOKEN_REFRESH_BUFFER =
parseInt(process.env.TOKEN_REFRESH_BUFFER) || 300;
this.config.MAX_CONCURRENT_REQUESTS =
parseInt(process.env.MAX_CONCURRENT_REQUESTS) || 10;
// Logging Configuration
this.config.LOG_LEVEL = process.env.LOG_LEVEL || "info";
this.config.LOG_FILE_PATH =
process.env.LOG_FILE_PATH || "./logs/mcp-server.log";
this.config.LOG_MAX_SIZE = process.env.LOG_MAX_SIZE || "10m";
this.config.LOG_MAX_FILES = process.env.LOG_MAX_FILES || "5";
this.config.LOG_DATE_PATTERN = process.env.LOG_DATE_PATTERN || "YYYY-MM-DD";
this.config.ENABLE_REQUEST_LOGGING =
process.env.ENABLE_REQUEST_LOGGING || "true";
this.config.MASK_SENSITIVE_DATA = process.env.MASK_SENSITIVE_DATA || "true";
// Error Handling
this.config.ENABLE_DETAILED_ERRORS =
process.env.ENABLE_DETAILED_ERRORS === "true";
this.config.HIPAA_COMPLIANCE_MODE =
process.env.HIPAA_COMPLIANCE_MODE !== "false";
this.config.ERROR_REPORTING_LEVEL =
process.env.ERROR_REPORTING_LEVEL || "production";
// Rate Limiting
this.config.RATE_LIMIT_ENABLED = process.env.RATE_LIMIT_ENABLED !== "false";
this.config.RATE_LIMIT_WINDOW =
parseInt(process.env.RATE_LIMIT_WINDOW) || 60000;
this.config.RATE_LIMIT_MAX_REQUESTS =
parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) || 100;
// Health Check
this.config.HEALTH_CHECK_ENABLED =
process.env.HEALTH_CHECK_ENABLED !== "false";
this.config.HEALTH_CHECK_INTERVAL =
parseInt(process.env.HEALTH_CHECK_INTERVAL) || 30000;
this.config.HEALTH_CHECK_ENDPOINT =
process.env.HEALTH_CHECK_ENDPOINT || "/health";
// Development Settings
this.config.NODE_ENV = process.env.NODE_ENV || "production";
this.config.DEBUG_MODE = process.env.DEBUG_MODE === "true";
this.config.ENABLE_CORS = process.env.ENABLE_CORS !== "false";
this.config.CORS_ORIGINS = process.env.CORS_ORIGINS || "*";
}
/**
* Define validation rules for configuration
* @private
* @returns {Object} Validation rules
*/
_defineValidationRules() {
return {
required: ["LARAVEL_API_BASE_URL"],
optional: ["MCP_SERVER_NAME", "MCP_SERVER_VERSION"],
authCredentials: [
"ADMIN_USERNAME",
"ADMIN_PASSWORD",
"AGENT_USERNAME",
"AGENT_PASSWORD",
"PATIENT_USERNAME",
"PATIENT_PASSWORD",
"PRACTITIONER_USERNAME",
"PRACTITIONER_PASSWORD",
"AFFILIATE_USERNAME",
"AFFILIATE_PASSWORD",
"PARTNER_USERNAME",
"PARTNER_PASSWORD",
"NETWORK_USERNAME",
"NETWORK_PASSWORD",
"DOCTOR_USERNAME",
"DOCTOR_PASSWORD",
"PROVIDER_USERNAME",
"PROVIDER_PASSWORD",
],
numeric: [
"LARAVEL_API_TIMEOUT",
"LARAVEL_API_RETRY_ATTEMPTS",
"LARAVEL_API_RETRY_DELAY",
"MCP_SERVER_PORT",
"TOKEN_CACHE_DURATION",
"TOKEN_REFRESH_BUFFER",
"MAX_CONCURRENT_REQUESTS",
],
urls: ["LARAVEL_API_BASE_URL"],
};
}
/**
* Validate configuration
* @private
* @throws {ConfigurationError} If validation fails
*/
_validateConfiguration() {
const errors = [];
const warnings = [];
// Check required fields
this.validationRules.required.forEach((key) => {
if (!this.config[key]) {
errors.push(`Required configuration missing: ${key}`);
}
});
// Validate URLs
this.validationRules.urls.forEach((key) => {
if (this.config[key]) {
try {
new URL(this.config[key]);
} catch (error) {
errors.push(`Invalid URL format for ${key}: ${this.config[key]}`);
}
}
});
// Validate numeric values
this.validationRules.numeric.forEach((key) => {
if (
this.config[key] !== undefined &&
(isNaN(this.config[key]) || this.config[key] < 0)
) {
errors.push(`Invalid numeric value for ${key}: ${this.config[key]}`);
}
});
// Check authentication credentials (warnings only)
const authTypes = [
"ADMIN",
"AGENT",
"PATIENT",
"PRACTITIONER",
"AFFILIATE",
"PARTNER",
"NETWORK",
"DOCTOR",
"PROVIDER",
];
authTypes.forEach((authType) => {
const usernameKey = `${authType}_USERNAME`;
const passwordKey = `${authType}_PASSWORD`;
if (!this.config[usernameKey] || !this.config[passwordKey]) {
warnings.push(`${authType} authentication credentials not configured`);
}
});
// Security warnings
if (
this.config.ENABLE_REQUEST_LOGGING === "true" &&
this.config.MASK_SENSITIVE_DATA !== "true"
) {
warnings.push(
"Request logging enabled without sensitive data masking - potential security risk"
);
}
if (this.config.HIPAA_COMPLIANCE_MODE !== true) {
warnings.push(
"HIPAA compliance mode disabled - ensure this is intentional for healthcare data"
);
}
if (
this.config.DEBUG_MODE === true &&
this.config.NODE_ENV === "production"
) {
warnings.push("Debug mode enabled in production environment");
}
// Log warnings
warnings.forEach((warning) =>
logger.warn(`Configuration warning: ${warning}`)
);
// Throw error if validation fails
if (errors.length > 0) {
const errorMessage = `Configuration validation failed:\n${errors.join(
"\n"
)}`;
logger.error(errorMessage);
throw new ConfigurationError(errorMessage, "VALIDATION_FAILED", {
errors,
warnings,
});
}
if (warnings.length > 0) {
logger.info(`Configuration loaded with ${warnings.length} warnings`);
}
}
/**
* Get configuration value
* @param {string} key - Configuration key
* @param {*} defaultValue - Default value if key not found
* @returns {*} Configuration value
*/
get(key, defaultValue = undefined) {
return this.config[key] !== undefined ? this.config[key] : defaultValue;
}
/**
* Set configuration value
* @param {string} key - Configuration key
* @param {*} value - Configuration value
*/
set(key, value) {
this.config[key] = value;
}
/**
* Get all configuration
* @param {boolean} includeSensitive - Include sensitive values
* @returns {Object} Configuration object
*/
getAll(includeSensitive = false) {
if (includeSensitive) {
return { ...this.config };
}
// Mask sensitive values
const masked = { ...this.config };
const sensitiveKeys = this.validationRules.authCredentials.filter((key) =>
key.includes("PASSWORD")
);
sensitiveKeys.forEach((key) => {
if (masked[key]) {
masked[key] = "[MASKED]";
}
});
return masked;
}
/**
* Check if configuration is valid
* @returns {boolean} True if configuration is valid
*/
isValid() {
try {
this._validateConfiguration();
return true;
} catch (error) {
return false;
}
}
/**
* Get configuration summary
* @returns {Object} Configuration summary
*/
getSummary() {
const authTypesConfigured = [];
const authTypes = [
"ADMIN",
"AGENT",
"PATIENT",
"PRACTITIONER",
"AFFILIATE",
"PARTNER",
"NETWORK",
"DOCTOR",
"PROVIDER",
];
authTypes.forEach((authType) => {
const usernameKey = `${authType}_USERNAME`;
const passwordKey = `${authType}_PASSWORD`;
if (this.config[usernameKey] && this.config[passwordKey]) {
authTypesConfigured.push(authType.toLowerCase());
}
});
return {
serverName: this.config.MCP_SERVER_NAME,
serverVersion: this.config.MCP_SERVER_VERSION,
apiBaseUrl: this.config.LARAVEL_API_BASE_URL,
authTypesConfigured,
hipaaCompliance: this.config.HIPAA_COMPLIANCE_MODE,
logLevel: this.config.LOG_LEVEL,
environment: this.config.NODE_ENV,
debugMode: this.config.DEBUG_MODE,
};
}
/**
* Reload configuration from environment
*/
reload() {
logger.info("Reloading configuration...");
this._loadConfiguration();
this._validateConfiguration();
logger.info("Configuration reloaded successfully");
}
}