no idea- added loki and other stuff to market-data-gateway, also added config lib
This commit is contained in:
parent
b957fb99aa
commit
1b71fc87ab
72 changed files with 6178 additions and 153 deletions
162
libs/config/src/core.ts
Normal file
162
libs/config/src/core.ts
Normal 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}`);
|
||||
}
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue