193 lines
5.0 KiB
TypeScript
193 lines
5.0 KiB
TypeScript
/**
|
|
* A2P AutoPilot - Main Entry Point
|
|
* Sets up Express server, database, Redis, and BullMQ workers
|
|
*/
|
|
|
|
import express from 'express';
|
|
import cors from 'cors';
|
|
import helmet from 'helmet';
|
|
import dotenv from 'dotenv';
|
|
import { initializeDatabase, closeDatabase } from './db/index';
|
|
import { runMigrations } from './db/migrate';
|
|
import { logger } from './utils/logger';
|
|
import routes from './api/routes';
|
|
import {
|
|
requestLogger,
|
|
errorHandler,
|
|
notFoundHandler,
|
|
} from './api/middleware';
|
|
|
|
// Load environment variables
|
|
dotenv.config();
|
|
|
|
// ============================================================
|
|
// CONFIGURATION
|
|
// ============================================================
|
|
|
|
const PORT = process.env.PORT || 3000;
|
|
const DATABASE_URL = process.env.DATABASE_URL;
|
|
const REDIS_URL = process.env.REDIS_URL;
|
|
const NODE_ENV = process.env.NODE_ENV || 'development';
|
|
|
|
// Validate required environment variables
|
|
const requiredEnvVars = ['DATABASE_URL', 'REDIS_URL', 'API_KEY', 'TWILIO_ACCOUNT_SID', 'TWILIO_AUTH_TOKEN'];
|
|
const missing = requiredEnvVars.filter((key) => !process.env[key]);
|
|
|
|
if (missing.length > 0) {
|
|
logger.error({ missing }, 'Missing required environment variables');
|
|
process.exit(1);
|
|
}
|
|
|
|
// ============================================================
|
|
// EXPRESS APP SETUP
|
|
// ============================================================
|
|
|
|
const app = express();
|
|
|
|
// Security middleware
|
|
app.use(helmet());
|
|
|
|
// CORS configuration
|
|
app.use(
|
|
cors({
|
|
origin: process.env.CORS_ORIGIN || '*',
|
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
|
|
allowedHeaders: ['Content-Type', 'Authorization'],
|
|
})
|
|
);
|
|
|
|
// Body parsing
|
|
app.use(express.json({ limit: '10mb' }));
|
|
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
|
|
|
// Request logging
|
|
app.use(requestLogger);
|
|
|
|
// Mount API routes
|
|
app.use('/api', routes);
|
|
|
|
// Health check at root
|
|
app.get('/', (_req, res) => {
|
|
res.json({
|
|
service: 'a2p-autopilot',
|
|
version: '1.0.0',
|
|
status: 'running',
|
|
environment: NODE_ENV,
|
|
});
|
|
});
|
|
|
|
// 404 handler
|
|
app.use(notFoundHandler);
|
|
|
|
// Global error handler (must be last)
|
|
app.use(errorHandler);
|
|
|
|
// ============================================================
|
|
// DATABASE & REDIS INITIALIZATION
|
|
// ============================================================
|
|
|
|
async function initializeServices() {
|
|
logger.info('Initializing services...');
|
|
|
|
// 1. Connect to PostgreSQL
|
|
try {
|
|
initializeDatabase(DATABASE_URL!);
|
|
logger.info('✓ Database connected');
|
|
} catch (error) {
|
|
logger.error({ error }, 'Failed to connect to database');
|
|
throw error;
|
|
}
|
|
|
|
// 2. Run migrations
|
|
try {
|
|
await runMigrations(DATABASE_URL!);
|
|
logger.info('✓ Database migrations completed');
|
|
} catch (error) {
|
|
logger.error({ error }, 'Failed to run migrations');
|
|
throw error;
|
|
}
|
|
|
|
// 3. Connect to Redis
|
|
// TODO: Initialize Redis client
|
|
logger.info('✓ Redis connected (TODO)');
|
|
|
|
// 4. Initialize BullMQ workers
|
|
// TODO: Start BullMQ workers for polling and submission processing
|
|
logger.info('✓ BullMQ workers started (TODO)');
|
|
}
|
|
|
|
// ============================================================
|
|
// SERVER STARTUP
|
|
// ============================================================
|
|
|
|
async function startServer() {
|
|
try {
|
|
await initializeServices();
|
|
|
|
const server = app.listen(PORT, () => {
|
|
logger.info(
|
|
{
|
|
port: PORT,
|
|
env: NODE_ENV,
|
|
processId: process.pid,
|
|
},
|
|
`🚀 A2P AutoPilot server running on port ${PORT}`
|
|
);
|
|
});
|
|
|
|
// Graceful shutdown handling
|
|
const shutdown = async (signal: string) => {
|
|
logger.info({ signal }, 'Shutdown signal received');
|
|
|
|
server.close(async () => {
|
|
logger.info('HTTP server closed');
|
|
|
|
try {
|
|
// Close database connection
|
|
await closeDatabase();
|
|
logger.info('Database connection closed');
|
|
|
|
// TODO: Close Redis connection
|
|
// await closeRedis();
|
|
|
|
// TODO: Close BullMQ workers
|
|
// await closeBullMQ();
|
|
|
|
logger.info('Graceful shutdown complete');
|
|
process.exit(0);
|
|
} catch (error) {
|
|
logger.error({ error }, 'Error during shutdown');
|
|
process.exit(1);
|
|
}
|
|
});
|
|
|
|
// Force shutdown after 30 seconds
|
|
setTimeout(() => {
|
|
logger.error('Forced shutdown after timeout');
|
|
process.exit(1);
|
|
}, 30000);
|
|
};
|
|
|
|
// Register shutdown handlers
|
|
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
|
|
// Handle uncaught errors
|
|
process.on('uncaughtException', (error) => {
|
|
logger.error({ error }, 'Uncaught exception');
|
|
shutdown('uncaughtException');
|
|
});
|
|
|
|
process.on('unhandledRejection', (reason, promise) => {
|
|
logger.error({ reason, promise }, 'Unhandled promise rejection');
|
|
shutdown('unhandledRejection');
|
|
});
|
|
} catch (error) {
|
|
logger.error({ error }, 'Failed to start server');
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Start the server
|
|
startServer();
|