no idea- added loki and other stuff to market-data-gateway, also added config lib

This commit is contained in:
Bojan Kucera 2025-06-03 11:37:58 -04:00
parent b957fb99aa
commit 1b71fc87ab
72 changed files with 6178 additions and 153 deletions

162
libs/config/src/core.ts Normal file
View file

@ -0,0 +1,162 @@
/**
* Core configuration module for the Stock Bot platform
*/
import { config as dotenvConfig } from 'dotenv';
import path from 'path';
import { z } from 'zod';
import { Environment } from './types';
/**
* Represents an error related to configuration validation
*/
export class ConfigurationError extends Error {
constructor(message: string) {
super(message);
this.name = 'ConfigurationError';
}
}
/**
* Loads environment variables from .env files based on the current environment
*/
export function loadEnvVariables(envOverride?: string): void {
const env = envOverride || process.env.NODE_ENV || 'development';
// Order of loading:
// 1. .env (base environment variables)
// 2. .env.{environment} (environment-specific variables)
// 3. .env.local (local overrides, not to be committed)
const envFiles = [
'.env',
`.env.${env}`,
'.env.local'
];
for (const file of envFiles) {
dotenvConfig({ path: path.resolve(process.cwd(), file) });
}
}
/**
* Gets the current environment from process.env.NODE_ENV
*/
export function getEnvironment(): Environment {
const env = process.env.NODE_ENV?.toLowerCase() || 'development';
switch (env) {
case 'development':
return Environment.Development;
case 'testing':
return Environment.Testing;
case 'staging':
return Environment.Staging;
case 'production':
return Environment.Production;
default:
return Environment.Development;
}
}
/**
* Validates configuration using Zod schema
*/
export function validateConfig<T>(config: unknown, schema: z.ZodSchema<T>): T {
try {
return schema.parse(config);
} catch (error) {
if (error instanceof z.ZodError) {
const issues = error.issues.map(issue =>
`${issue.path.join('.')}: ${issue.message}`
).join('\n');
throw new ConfigurationError(`Configuration validation failed:\n${issues}`);
}
throw new ConfigurationError('Invalid configuration');
}
}
/**
* Retrieves an environment variable with validation
*/
export function getEnvVar(key: string, required: boolean = false): string | undefined {
const value = process.env[key];
if (required && (value === undefined || value === '')) {
throw new ConfigurationError(`Required environment variable ${key} is missing`);
}
return value;
}
/**
* Retrieves a numeric environment variable with validation
*/
export function getNumericEnvVar(key: string, defaultValue?: number): number {
const value = process.env[key];
if (value === undefined || value === '') {
if (defaultValue !== undefined) {
return defaultValue;
}
throw new ConfigurationError(`Required numeric environment variable ${key} is missing`);
}
const numValue = Number(value);
if (isNaN(numValue)) {
throw new ConfigurationError(`Environment variable ${key} is not a valid number`);
}
return numValue;
}
/**
* Retrieves a boolean environment variable with validation
*/
export function getBooleanEnvVar(key: string, defaultValue?: boolean): boolean {
const value = process.env[key];
if (value === undefined || value === '') {
if (defaultValue !== undefined) {
return defaultValue;
}
throw new ConfigurationError(`Required boolean environment variable ${key} is missing`);
}
return value.toLowerCase() === 'true' || value === '1';
}
/**
* Creates a typed dynamic configuration loader for a specific service
*/
export function createConfigLoader<T>(
serviceName: string,
schema: z.ZodSchema<T>,
defaultConfig: Partial<T> = {}
): () => T {
return (): T => {
try {
loadEnvVariables();
const configEnvVar = `${serviceName.toUpperCase()}_CONFIG`;
let config = { ...defaultConfig } as unknown as T;
// Try to load JSON from environment variable if available
const configJson = process.env[configEnvVar];
if (configJson) {
try {
const parsedConfig = JSON.parse(configJson);
config = { ...config, ...parsedConfig };
} catch (error) {
throw new ConfigurationError(`Invalid JSON in ${configEnvVar} environment variable`);
}
}
// Validate and return the config
return validateConfig(config, schema);
} catch (error) {
if (error instanceof ConfigurationError) {
throw error;
}
throw new ConfigurationError(`Failed to load configuration for service ${serviceName}: ${error}`);
}
};
}