Phase 2 - Monitoring & Observability: - Create metrics collection system with counters, gauges, and histograms - Add Prometheus-compatible /metrics endpoint - Implement request/response metrics tracking - Database and process metrics monitoring Phase 2 - Enhanced Error Handling: - Circuit breaker pattern for service resilience - Retry mechanism with exponential backoff - Comprehensive error handler middleware - Async error wrapper for route handlers - Request timeout middleware Phase 3 - UI Components: - SkeletonLoader components for better loading states - EmptyState component with helpful messages - ErrorState component with retry functionality - Enhanced DataTable with sorting, filtering, pagination All components are production-ready and integrated.
151 lines
4.4 KiB
TypeScript
151 lines
4.4 KiB
TypeScript
/**
|
|
* REST API Server
|
|
* Provides RESTful API for Brazil SWIFT Operations Platform
|
|
*/
|
|
|
|
import 'dotenv/config';
|
|
import express, { Express, Request, Response, NextFunction } from 'express';
|
|
import cors from 'cors';
|
|
import { getLogger } from '@brazil-swift-ops/utils';
|
|
import { evaluateTransaction } from '@brazil-swift-ops/rules-engine';
|
|
import type { Transaction } from '@brazil-swift-ops/types';
|
|
import { initializeDatabase, closeDatabase } from './db/connection';
|
|
import { runMigrations } from './db/migrate';
|
|
import { seedDatabase } from './db/seed';
|
|
import authRoutes from './routes/auth';
|
|
|
|
const app: Express = express();
|
|
const logger = getLogger();
|
|
const PORT = process.env.PORT || 3000;
|
|
|
|
// Middleware
|
|
app.use(cors());
|
|
app.use(express.json());
|
|
app.use((req: Request, res: Response, next: NextFunction) => {
|
|
const correlationId = `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
logger.setCorrelationId(correlationId);
|
|
req.headers['x-correlation-id'] = correlationId;
|
|
next();
|
|
});
|
|
|
|
// Health checks
|
|
import { healthCheckHandler, readinessCheckHandler, livenessCheckHandler } from './health';
|
|
|
|
app.get('/health', healthCheckHandler);
|
|
app.get('/health/ready', readinessCheckHandler);
|
|
app.get('/health/live', livenessCheckHandler);
|
|
|
|
// Import all route handlers
|
|
import transactionRoutes from './routes/transactions';
|
|
import accountRoutes from './routes/accounts';
|
|
import userRoutes from './routes/users';
|
|
import complianceRoutes from './routes/compliance';
|
|
import reportsRoutes from './routes/reports';
|
|
import fxContractRoutes from './routes/fx-contracts';
|
|
import metricsRoutes from './routes/metrics';
|
|
import { errorHandler } from './middleware/errorHandler';
|
|
import { incrementCounter, recordHistogram } from './monitoring/metrics';
|
|
|
|
// Request metrics middleware
|
|
app.use((req: Request, res: Response, next: NextFunction) => {
|
|
const startTime = Date.now();
|
|
incrementCounter('http_requests_total', { method: req.method, path: req.path });
|
|
|
|
res.on('finish', () => {
|
|
const duration = Date.now() - startTime;
|
|
recordHistogram('http_request_duration_ms', duration, {
|
|
method: req.method,
|
|
status: res.statusCode.toString(),
|
|
});
|
|
incrementCounter('http_responses_total', {
|
|
method: req.method,
|
|
status: res.statusCode.toString(),
|
|
});
|
|
});
|
|
|
|
next();
|
|
});
|
|
|
|
// Register routes
|
|
app.use('/api/v1/auth', authRoutes);
|
|
app.use('/api/v1/transactions', transactionRoutes);
|
|
app.use('/api/v1/accounts', accountRoutes);
|
|
app.use('/api/v1/users', userRoutes);
|
|
app.use('/api/v1/compliance', complianceRoutes);
|
|
app.use('/api/v1/reports', reportsRoutes);
|
|
app.use('/api/v1/fx-contracts', fxContractRoutes);
|
|
app.use('/metrics', metricsRoutes);
|
|
|
|
// Legacy evaluate transaction endpoint
|
|
app.post('/api/v1/transactions/evaluate', async (req: Request, res: Response) => {
|
|
try {
|
|
const transaction = req.body as Transaction;
|
|
const result = evaluateTransaction(transaction);
|
|
|
|
logger.info('Transaction evaluated', { transactionId: transaction.id });
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result,
|
|
});
|
|
} catch (error) {
|
|
logger.error('Error evaluating transaction', error as Error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Failed to evaluate transaction',
|
|
});
|
|
}
|
|
});
|
|
|
|
// Error handler (must be last)
|
|
app.use(errorHandler);
|
|
|
|
// Initialize database and start server
|
|
let server: any;
|
|
|
|
async function startServer() {
|
|
try {
|
|
// Initialize database
|
|
await initializeDatabase();
|
|
console.log('✓ Database initialized');
|
|
|
|
// Run migrations
|
|
await runMigrations();
|
|
console.log('✓ Migrations completed');
|
|
|
|
// Seed database with initial data
|
|
await seedDatabase();
|
|
console.log('✓ Database seeded');
|
|
|
|
// Start server
|
|
server = app.listen(PORT, () => {
|
|
logger.info(`API server started on port ${PORT}`);
|
|
});
|
|
|
|
// Handle graceful shutdown
|
|
process.on('SIGTERM', shutdown);
|
|
process.on('SIGINT', shutdown);
|
|
} catch (error) {
|
|
console.error('Failed to start server:', error);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
async function shutdown() {
|
|
console.log('\nShutting down gracefully...');
|
|
if (server) {
|
|
server.close(async () => {
|
|
await closeDatabase();
|
|
console.log('Server closed');
|
|
process.exit(0);
|
|
});
|
|
}
|
|
}
|
|
|
|
// Start server (only if running directly, not when imported)
|
|
if (typeof require !== 'undefined' && require.main === module) {
|
|
startServer();
|
|
}
|
|
|
|
export default app;
|