Phase 1a - Database Setup: - Add PostgreSQL connection pooling with pg client - Create 8 SQL migrations for all database schemas - Implement migration execution system with tracking - Add environment configuration for database and JWT settings Phase 1b - Authentication & Authorization: - Implement password hashing with bcrypt - Create JWT token generation (access + refresh tokens) - Implement RBAC with 5 roles (Admin, Manager, Analyst, Auditor, Viewer) - Create auth middleware for authentication and authorization - Add auth routes (login, register, refresh, logout, profile) Phase 1c - API Endpoints (Full CRUD): - Transaction endpoints with evaluation and batch processing - Account management (treasury and subledger accounts) - User management (admin-only) - FX contract management - Compliance endpoints (rules, results, thresholds) - Reporting endpoints (summary, compliance, audit logs) - Health check endpoints with database status Phase 1d - Data Seeding: - Create database seeding system with roles, permissions, users - Add sample data (treasury accounts, FX contracts) - Implement admin user creation from environment variables All endpoints protected with authentication and role-based access control.
180 lines
6.6 KiB
TypeScript
180 lines
6.6 KiB
TypeScript
/**
|
|
* Configuration management
|
|
* Externalizes configuration from environment variables and config files
|
|
*/
|
|
|
|
export interface AppConfig {
|
|
// Application
|
|
appName: string;
|
|
appVersion: string;
|
|
environment: 'development' | 'staging' | 'production';
|
|
port?: number;
|
|
|
|
// Institution
|
|
institutionBIC: string;
|
|
institutionName: string;
|
|
institutionCountry: string;
|
|
|
|
// Regulatory
|
|
reportingThresholdUSD: number;
|
|
amlStructuringThresholdUSD: number;
|
|
amlStructuringWindowDays: number;
|
|
|
|
// IOF Rates
|
|
iofRateInbound: number;
|
|
iofRateOutbound: number;
|
|
|
|
// FX Rates
|
|
fxRateProvider: 'hardcoded' | 'central-bank' | 'bloomberg' | 'reuters' | 'xe';
|
|
fxRateCacheTTL: number; // seconds
|
|
|
|
// Database (when implemented)
|
|
databaseUrl?: string;
|
|
databasePoolSize?: number;
|
|
|
|
// Logging
|
|
logLevel: 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
|
logFormat: 'json' | 'text';
|
|
|
|
// Security
|
|
enableAuth: boolean;
|
|
sessionSecret?: string;
|
|
jwtSecret?: string;
|
|
jwtRefreshSecret?: string;
|
|
jwtExpiryMinutes?: number;
|
|
jwtRefreshExpiryDays?: number;
|
|
|
|
// Admin credentials (for initial setup)
|
|
adminEmail?: string;
|
|
adminPassword?: string;
|
|
|
|
// BCB Reporting
|
|
bcbReportingEnabled: boolean;
|
|
bcbReportingApiUrl?: string;
|
|
bcbReportingApiKey?: string;
|
|
|
|
// Audit
|
|
auditRetentionDays: number;
|
|
auditAutoDelete: boolean;
|
|
}
|
|
|
|
/**
|
|
* Load configuration from environment variables
|
|
*/
|
|
export function loadConfig(): AppConfig {
|
|
return {
|
|
appName: process.env.APP_NAME || 'Brazil SWIFT Operations',
|
|
appVersion: process.env.APP_VERSION || '1.0.0',
|
|
environment: ((typeof process !== 'undefined' ? process.env?.NODE_ENV : undefined) as any) || 'development',
|
|
port: typeof process !== 'undefined' ? parseInt(process.env?.PORT || '3000', 10) : 3000,
|
|
|
|
institutionBIC: (typeof process !== 'undefined' ? process.env?.INSTITUTION_BIC : undefined) || 'ESTRBRRJ',
|
|
institutionName: (typeof process !== 'undefined' ? process.env?.INSTITUTION_NAME : undefined) || 'Strategy Investimentos S/A CVC',
|
|
institutionCountry: (typeof process !== 'undefined' ? process.env?.INSTITUTION_COUNTRY : undefined) || 'BR',
|
|
|
|
reportingThresholdUSD: parseFloat((typeof process !== 'undefined' ? process.env?.REPORTING_THRESHOLD_USD : undefined) || '10000'),
|
|
amlStructuringThresholdUSD: parseFloat((typeof process !== 'undefined' ? process.env?.AML_STRUCTURING_THRESHOLD_USD : undefined) || '10000'),
|
|
amlStructuringWindowDays: parseInt((typeof process !== 'undefined' ? process.env?.AML_STRUCTURING_WINDOW_DAYS : undefined) || '30', 10),
|
|
|
|
iofRateInbound: parseFloat((typeof process !== 'undefined' ? process.env?.IOF_RATE_INBOUND : undefined) || '0.0038'),
|
|
iofRateOutbound: parseFloat((typeof process !== 'undefined' ? process.env?.IOF_RATE_OUTBOUND : undefined) || '0.035'),
|
|
|
|
fxRateProvider: ((typeof process !== 'undefined' ? process.env?.FX_RATE_PROVIDER : undefined) as any) || 'hardcoded',
|
|
fxRateCacheTTL: parseInt((typeof process !== 'undefined' ? process.env?.FX_RATE_CACHE_TTL : undefined) || '3600', 10),
|
|
|
|
databaseUrl: typeof process !== 'undefined' ? process.env?.DATABASE_URL : undefined,
|
|
databasePoolSize: parseInt((typeof process !== 'undefined' ? process.env?.DATABASE_POOL_SIZE : undefined) || '10', 10),
|
|
|
|
logLevel: ((typeof process !== 'undefined' ? process.env?.LOG_LEVEL : undefined) as any) || 'info',
|
|
logFormat: ((typeof process !== 'undefined' ? process.env?.LOG_FORMAT : undefined) as any) || 'json',
|
|
|
|
enableAuth: (typeof process !== 'undefined' ? process.env?.ENABLE_AUTH : undefined) === 'true',
|
|
sessionSecret: typeof process !== 'undefined' ? process.env?.SESSION_SECRET : undefined,
|
|
jwtSecret: typeof process !== 'undefined' ? process.env?.JWT_SECRET : undefined,
|
|
jwtRefreshSecret: typeof process !== 'undefined' ? process.env?.JWT_REFRESH_SECRET : undefined,
|
|
jwtExpiryMinutes: parseInt((typeof process !== 'undefined' ? process.env?.JWT_EXPIRY_MINUTES : undefined) || '15', 10),
|
|
jwtRefreshExpiryDays: parseInt((typeof process !== 'undefined' ? process.env?.JWT_REFRESH_EXPIRY_DAYS : undefined) || '7', 10),
|
|
|
|
adminEmail: typeof process !== 'undefined' ? process.env?.ADMIN_EMAIL : undefined,
|
|
adminPassword: typeof process !== 'undefined' ? process.env?.ADMIN_PASSWORD : undefined,
|
|
|
|
bcbReportingEnabled: (typeof process !== 'undefined' ? process.env?.BCB_REPORTING_ENABLED : undefined) === 'true',
|
|
bcbReportingApiUrl: typeof process !== 'undefined' ? process.env?.BCB_REPORTING_API_URL : undefined,
|
|
bcbReportingApiKey: typeof process !== 'undefined' ? process.env?.BCB_REPORTING_API_KEY : undefined,
|
|
|
|
auditRetentionDays: parseInt((typeof process !== 'undefined' ? process.env?.AUDIT_RETENTION_DAYS : undefined) || '2555', 10), // 7 years
|
|
auditAutoDelete: (typeof process !== 'undefined' ? process.env?.AUDIT_AUTO_DELETE : undefined) === 'true',
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Validate configuration
|
|
*/
|
|
export function validateConfig(config: AppConfig): { valid: boolean; errors: string[] } {
|
|
const errors: string[] = [];
|
|
|
|
if (!config.institutionBIC || config.institutionBIC.length !== 8) {
|
|
errors.push('Institution BIC must be 8 characters');
|
|
}
|
|
|
|
if (config.reportingThresholdUSD <= 0) {
|
|
errors.push('Reporting threshold must be greater than 0');
|
|
}
|
|
|
|
if (config.iofRateInbound < 0 || config.iofRateInbound > 1) {
|
|
errors.push('IOF inbound rate must be between 0 and 1');
|
|
}
|
|
|
|
if (config.iofRateOutbound < 0 || config.iofRateOutbound > 1) {
|
|
errors.push('IOF outbound rate must be between 0 and 1');
|
|
}
|
|
|
|
if (config.auditRetentionDays < 0) {
|
|
errors.push('Audit retention days must be non-negative');
|
|
}
|
|
|
|
if (config.enableAuth && !config.sessionSecret && !config.jwtSecret) {
|
|
errors.push('Authentication enabled but no session or JWT secret provided');
|
|
}
|
|
|
|
if (config.bcbReportingEnabled && !config.bcbReportingApiUrl) {
|
|
errors.push('BCB reporting enabled but no API URL provided');
|
|
}
|
|
|
|
return {
|
|
valid: errors.length === 0,
|
|
errors,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get default configuration (for development)
|
|
*/
|
|
export function getDefaultConfig(): AppConfig {
|
|
return loadConfig();
|
|
}
|
|
|
|
// Singleton instance
|
|
let configInstance: AppConfig | null = null;
|
|
|
|
/**
|
|
* Get configuration singleton
|
|
*/
|
|
export function getConfig(): AppConfig {
|
|
if (!configInstance) {
|
|
configInstance = loadConfig();
|
|
const validation = validateConfig(configInstance);
|
|
if (!validation.valid) {
|
|
console.warn('Configuration validation errors:', validation.errors);
|
|
}
|
|
}
|
|
return configInstance;
|
|
}
|
|
|
|
/**
|
|
* Reset configuration (useful for testing)
|
|
*/
|
|
export function resetConfig(): void {
|
|
configInstance = null;
|
|
}
|