Re_Backend/_archive/services/configReader.service.ts

161 lines
4.8 KiB
TypeScript

/**
* Configuration Reader Service
* Reads admin configurations from database for use in backend logic
*/
import { sequelize } from '@config/database';
import { QueryTypes } from 'sequelize';
import logger from '@utils/logger';
// Cache configurations in memory for performance
let configCache: Map<string, string> = new Map();
let cacheExpiry: Date | null = null;
const CACHE_DURATION_MS = 5 * 60 * 1000; // 5 minutes
// Sensitive config keys that should be masked in logs
const SENSITIVE_CONFIG_PATTERNS = [
'API_KEY', 'SECRET', 'PASSWORD', 'TOKEN', 'CREDENTIAL',
'PRIVATE', 'AUTH', 'KEY', 'VAPID'
];
/**
* Check if a config key contains sensitive data
*/
function isSensitiveConfig(configKey: string): boolean {
const upperKey = configKey.toUpperCase();
return SENSITIVE_CONFIG_PATTERNS.some(pattern => upperKey.includes(pattern));
}
/**
* Mask sensitive value for logging (show first 4 and last 2 chars)
*/
function maskSensitiveValue(value: string): string {
if (!value || value.length <= 8) {
return '***REDACTED***';
}
return `${value.substring(0, 4)}****${value.substring(value.length - 2)}`;
}
/**
* Get a configuration value from database (with caching)
*/
export async function getConfigValue(configKey: string, defaultValue: string = ''): Promise<string> {
try {
// Check cache first
if (configCache.has(configKey) && cacheExpiry && new Date() < cacheExpiry) {
return configCache.get(configKey)!;
}
// Query database
const result = await sequelize.query(`
SELECT config_value
FROM admin_configurations
WHERE config_key = :configKey
LIMIT 1
`, {
replacements: { configKey },
type: QueryTypes.SELECT
});
if (result && result.length > 0) {
const value = (result[0] as any).config_value;
configCache.set(configKey, value);
// Always update cache expiry when loading from database
cacheExpiry = new Date(Date.now() + CACHE_DURATION_MS);
// Mask sensitive values in logs for security
const logValue = isSensitiveConfig(configKey) ? maskSensitiveValue(value) : value;
logger.info(`[ConfigReader] Loaded config '${configKey}' = '${logValue}' from database (cached for 5min)`);
return value;
}
// Mask sensitive default values in logs for security
const logDefault = isSensitiveConfig(configKey) ? maskSensitiveValue(defaultValue) : defaultValue;
logger.warn(`[ConfigReader] Config key '${configKey}' not found, using default: ${logDefault}`);
return defaultValue;
} catch (error) {
logger.error(`[ConfigReader] Error reading config '${configKey}':`, error);
return defaultValue;
}
}
/**
* Get number configuration
*/
export async function getConfigNumber(configKey: string, defaultValue: number): Promise<number> {
const value = await getConfigValue(configKey, String(defaultValue));
return parseFloat(value) || defaultValue;
}
/**
* Get boolean configuration
*/
export async function getConfigBoolean(configKey: string, defaultValue: boolean): Promise<boolean> {
const value = await getConfigValue(configKey, String(defaultValue));
return value === 'true' || value === '1';
}
/**
* Get TAT thresholds from database
*/
export async function getTatThresholds(): Promise<{ first: number; second: number }> {
const first = await getConfigNumber('TAT_REMINDER_THRESHOLD_1', 50);
const second = await getConfigNumber('TAT_REMINDER_THRESHOLD_2', 75);
return { first, second };
}
/**
* Get working hours from database
*/
export async function getWorkingHours(): Promise<{ startHour: number; endHour: number }> {
const startHour = await getConfigNumber('WORK_START_HOUR', 9);
const endHour = await getConfigNumber('WORK_END_HOUR', 18);
return { startHour, endHour };
}
/**
* Clear configuration cache (call after updating configs)
*/
export function clearConfigCache(): void {
configCache.clear();
cacheExpiry = null;
logger.info('[ConfigReader] Configuration cache cleared');
}
/**
* Preload all configurations into cache
*/
export async function preloadConfigurations(): Promise<void> {
try {
const results = await sequelize.query(`
SELECT config_key, config_value
FROM admin_configurations
`, { type: QueryTypes.SELECT });
results.forEach((row: any) => {
configCache.set(row.config_key, row.config_value);
});
cacheExpiry = new Date(Date.now() + CACHE_DURATION_MS);
logger.info(`[ConfigReader] Preloaded ${results.length} configurations into cache`);
} catch (error) {
logger.error('[ConfigReader] Error preloading configurations:', error);
}
}
/**
* Get Vertex AI configurations
*/
export async function getVertexAIConfig(): Promise<{
enabled: boolean;
}> {
const enabled = await getConfigBoolean('AI_ENABLED', true);
return { enabled };
}