forked from rohit/spurrin-backend
293 lines
9.4 KiB
JavaScript
293 lines
9.4 KiB
JavaScript
const express = require('express');
|
|
const cors = require('cors');
|
|
const compression = require('compression');
|
|
const path = require('path');
|
|
const dotenv = require('dotenv');
|
|
const helmet = require('helmet');
|
|
const initializeDatabase = require('./config/initDatabase');
|
|
const pm2 = require('pm2');
|
|
const fs = require('fs');
|
|
|
|
// Load environment variables
|
|
dotenv.config();
|
|
|
|
console.log('Current Working Directory:', process.cwd());
|
|
console.log('__dirname:', __dirname);
|
|
|
|
// Import configurations
|
|
const config = require('./config');
|
|
const { securityHeaders, apiLimiter, validateRequest, corsOptions } = require('./middlewares/security');
|
|
const { errorHandler } = require('./middlewares/errorHandler');
|
|
const logger = require('./utils/logger');
|
|
const monitoring = require('./utils/monitoring');
|
|
|
|
// Import routes
|
|
const authRoutes = require('./routes/auth');
|
|
const hospitalRoutes = require('./routes/hospitals');
|
|
const userRoutes = require('./routes/users');
|
|
const superAdminRoutes = require('./routes/superAdmins');
|
|
const documentRoutes = require('./routes/documents');
|
|
const pdfRoutes = require('./routes/pdfRoutes');
|
|
const onboardingRoutes = require('./routes/onboarding');
|
|
const appUserRoutes = require('./routes/appUsers');
|
|
const excelDataRoutes = require('./routes/exceldata');
|
|
const feedbackRoute = require('./routes/feedbacks');
|
|
const analyticsRoute = require('./routes/analysis');
|
|
const appVersionRoutes = require('./routes/appVersioning');
|
|
|
|
// Import services
|
|
const { refreshExpiredTokens } = require('./services/cronJobs');
|
|
const { repopulateQueueOnStartup } = require('./controllers/documentsController');
|
|
require('./services/webSocket');
|
|
require('./services/secondaryWebsocket');
|
|
|
|
// Create Express app
|
|
const app = express();
|
|
|
|
// Apply security middleware
|
|
app.use(helmet());
|
|
app.use(securityHeaders);
|
|
app.use(compression({
|
|
level: 6,
|
|
threshold: 1024,
|
|
filter: (req, res) => {
|
|
const contentType = res.getHeader('Content-Type');
|
|
return /text|json|javascript|css/.test(contentType);
|
|
}
|
|
}));
|
|
|
|
// Apply rate limiting to all API routes
|
|
app.use('/api/', apiLimiter);
|
|
|
|
// Apply CORS
|
|
app.use(cors({
|
|
origin: true, // Allow all origins
|
|
credentials: true,
|
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
|
|
exposedHeaders: ['Content-Range', 'X-Content-Range'],
|
|
maxAge: 86400
|
|
}));
|
|
|
|
// Request validation
|
|
app.use(validateRequest);
|
|
|
|
// Body parsing middleware
|
|
app.use(express.json());
|
|
app.use(express.urlencoded({ extended: true }));
|
|
|
|
// Static files
|
|
app.use('/uploads', express.static(path.join(__dirname, '..', 'uploads')));
|
|
app.use('/public', express.static(path.join(__dirname, '..', 'public')));
|
|
|
|
// Request logging middleware
|
|
app.use((req, res, next) => {
|
|
const start = Date.now();
|
|
res.on('finish', () => {
|
|
const duration = Date.now() - start;
|
|
monitoring.trackRequest(req.path, req.method, res.statusCode, duration);
|
|
logger.info(`${req.method} ${req.path} ${res.statusCode} - ${duration}ms`);
|
|
});
|
|
next();
|
|
});
|
|
|
|
// Initialize database before starting the server
|
|
async function startServer() {
|
|
try {
|
|
// Initialize database
|
|
await initializeDatabase();
|
|
console.log('Database initialized successfully');
|
|
|
|
// API routes
|
|
app.use('/api/auth', authRoutes);
|
|
app.use('/api/hospitals', hospitalRoutes);
|
|
app.use('/api/users', userRoutes);
|
|
app.use('/api/superAdmins', superAdminRoutes);
|
|
app.use('/api/onboarding', onboardingRoutes);
|
|
app.use('/api/documents', documentRoutes);
|
|
app.use('/api/pdf', pdfRoutes);
|
|
app.use('/api/app_users', appUserRoutes);
|
|
app.use('/api/process_excel', excelDataRoutes);
|
|
app.use('/api/feedbacks', feedbackRoute);
|
|
app.use('/api/analytics', analyticsRoute);
|
|
app.use('/api/app-versioning', appVersionRoutes);
|
|
|
|
// Health check endpoint
|
|
app.get('/health', (req, res) => {
|
|
res.json(monitoring.getHealthStatus());
|
|
});
|
|
|
|
// Database sync endpoint (protected by environment check)
|
|
app.post('/api/sync-database', async (req, res) => {
|
|
try {
|
|
// Only allow in development or with proper authentication
|
|
if (process.env.NODE_ENV === 'development' || req.headers['x-sync-token'] === process.env.DB_SYNC_TOKEN) {
|
|
await initializeDatabase();
|
|
res.json({ message: 'Database synchronized successfully' });
|
|
} else {
|
|
res.status(403).json({ error: 'Unauthorized' });
|
|
}
|
|
} catch (error) {
|
|
logger.error('Database sync failed:', error);
|
|
res.status(500).json({ error: 'Database synchronization failed' });
|
|
}
|
|
});
|
|
|
|
// Root endpoint
|
|
app.get('/', (req, res) => {
|
|
res.send("SpurrinAI Backend is running!!!");
|
|
});
|
|
|
|
app.get('/logs', (req, res) => {
|
|
pm2.connect(err => {
|
|
if (err) {
|
|
return res.status(500).send(generateErrorPage('Failed to connect to PM2', err.message));
|
|
}
|
|
|
|
pm2.list((err, processes) => {
|
|
if (err) {
|
|
return res.status(500).send(generateErrorPage('Failed to list processes', err.message));
|
|
}
|
|
|
|
const logs = processes.map(proc => {
|
|
const logFilePath = path.join(process.env.HOME, '.pm2/logs', `${proc.name}-out.log`);
|
|
const errorFilePath = path.join(process.env.HOME, '.pm2/logs', `${proc.name}-error.log`);
|
|
|
|
const stdoutSize = fs.existsSync(logFilePath) ? fs.statSync(logFilePath).size : 0;
|
|
const stderrSize = fs.existsSync(errorFilePath) ? fs.statSync(errorFilePath).size : 0;
|
|
const maxSize = 10 * 1024 * 1024; // 10MB max size for progress bar
|
|
|
|
return {
|
|
name: proc.name,
|
|
logs: {
|
|
stdout: fs.existsSync(logFilePath) ? fs.readFileSync(logFilePath, 'utf8') : 'No logs available',
|
|
stderr: fs.existsSync(errorFilePath) ? fs.readFileSync(errorFilePath, 'utf8') : 'No error logs available',
|
|
stdoutProgress: Math.min((stdoutSize / maxSize) * 100, 100),
|
|
stderrProgress: Math.min((stderrSize / maxSize) * 100, 100)
|
|
}
|
|
};
|
|
});
|
|
|
|
const logsHtml = logs.map(log => `
|
|
<div class="log-container">
|
|
<h2>${log.name}</h2>
|
|
<div class="stdout">
|
|
<h3>Standard Output</h3>
|
|
<progress value="${log.logs.stdoutProgress}" max="100"></progress>
|
|
<pre>${log.logs.stdout}</pre>
|
|
</div>
|
|
<div class="stderr">
|
|
<h3>Error Output</h3>
|
|
<progress value="${log.logs.stderrProgress}" max="100"></progress>
|
|
<pre>${log.logs.stderr}</pre>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
|
|
res.send(`
|
|
<html>
|
|
<head>
|
|
<title>PM2 Logs</title>
|
|
<style>
|
|
body { font-family: Arial, sans-serif; margin: 20px; }
|
|
h1 { color: #333; }
|
|
.log-container { margin-bottom: 20px; }
|
|
.log-container h2 { margin: 0; font-size: 1.2em; color: #444; }
|
|
.log-container pre { background: #f4f4f4; padding: 10px; border-radius: 5px; white-space: pre-wrap; word-wrap: break-word; }
|
|
.log-container progress { width: 100%; height: 20px; margin-bottom: 10px; }
|
|
.log-container .stdout { border-left: 5px solid #4CAF50; }
|
|
.log-container .stderr { border-left: 5px solid #f44336; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>PM2 Logs</h1>
|
|
${logsHtml}
|
|
</body>
|
|
</html>
|
|
`);
|
|
pm2.disconnect();
|
|
});
|
|
});
|
|
});
|
|
|
|
function generateErrorPage(title, message) {
|
|
return `
|
|
<html>
|
|
<head><title>${title}</title></head>
|
|
<body>
|
|
<h1>${title}</h1>
|
|
<p>${message}</p>
|
|
</body>
|
|
</html>
|
|
`;
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('checking automation!');
|
|
console.log('checking automation!');
|
|
console.log('checking automation!');
|
|
console.log('checking automation!');
|
|
console.log('checking automation!');
|
|
|
|
// Error handling middleware
|
|
app.use(errorHandler);
|
|
|
|
// Start server
|
|
const PORT = config.server.port;
|
|
const server = app.listen(PORT, () => {
|
|
logger.info(`Server is running on http://localhost:${PORT}`);
|
|
|
|
// Initialize background tasks
|
|
refreshExpiredTokens();
|
|
// repopulateQueueOnStartup();
|
|
});
|
|
|
|
// Graceful shutdown
|
|
const gracefulShutdown = async () => {
|
|
logger.info('Received shutdown signal');
|
|
|
|
// Close server
|
|
server.close(() => {
|
|
logger.info('HTTP server closed');
|
|
});
|
|
|
|
// Close database connections
|
|
const db = require('./config/database');
|
|
await db.closePool();
|
|
|
|
// Close WebSocket connections
|
|
const wss = require('./services/webSocket');
|
|
wss.close(() => {
|
|
logger.info('WebSocket server closed');
|
|
});
|
|
|
|
process.exit(0);
|
|
};
|
|
|
|
process.on('SIGTERM', gracefulShutdown);
|
|
process.on('SIGINT', gracefulShutdown);
|
|
|
|
// Handle uncaught exceptions
|
|
process.on('uncaughtException', (error) => {
|
|
logger.error('Uncaught Exception:', error);
|
|
gracefulShutdown();
|
|
});
|
|
|
|
// Handle unhandled promise rejections
|
|
process.on('unhandledRejection', (reason, promise) => {
|
|
logger.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
gracefulShutdown();
|
|
});
|
|
|
|
return server;
|
|
} catch (error) {
|
|
console.error('Failed to start server:', error);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
startServer();
|
|
|
|
module.exports = app; |