Files
R00TS/server/server.js

177 lines
5.1 KiB
JavaScript

require('dotenv').config();
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const morgan = require('morgan');
const path = require('path');
const fs = require('fs');
const { CronJob } = require('cron');
// Create logs directory if it doesn't exist
const logsDir = path.join(__dirname, 'logs');
if (!fs.existsSync(logsDir)) {
fs.mkdirSync(logsDir, { recursive: true });
console.log(`Created logs directory at ${logsDir}`);
}
// Configure logging
const logStream = fs.createWriteStream(
path.join(logsDir, `server-${new Date().toISOString().split('T')[0]}.log`),
{ flags: 'a' }
);
// Redirect console output to log file and console
const originalConsoleLog = console.log;
const originalConsoleError = console.error;
console.log = function() {
const args = Array.from(arguments);
const timestamp = new Date().toISOString();
const logMessage = `[${timestamp}] INFO: ${args.join(' ')}
`;
logStream.write(logMessage);
originalConsoleLog.apply(console, arguments);
};
console.error = function() {
const args = Array.from(arguments);
const timestamp = new Date().toISOString();
const logMessage = `[${timestamp}] ERROR: ${args.join(' ')}
`;
logStream.write(logMessage);
originalConsoleError.apply(console, arguments);
};
// Import routes
const wordRoutes = require('./routes/words');
const datasetRoutes = require('./routes/datasets');
const app = express();
const PORT = process.env.PORT || 5000;
// Middleware
app.use(cors());
app.use(express.json());
app.use(morgan('dev'));
// Serve static files from the React app
app.use(express.static(path.join(__dirname, '..')));
// Connect to MongoDB with retry logic
const connectWithRetry = () => {
console.log('Attempting to connect to MongoDB...');
mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log('MongoDB connected successfully');
// Initialize scheduled tasks after successful connection
initializeScheduledTasks();
})
.catch(err => {
console.error('MongoDB connection error:', err);
console.log('Retrying connection in 5 seconds...');
setTimeout(connectWithRetry, 5000);
});
};
// Handle MongoDB disconnection (auto-reconnect)
mongoose.connection.on('disconnected', () => {
console.log('MongoDB disconnected! Attempting to reconnect...');
connectWithRetry();
});
// Initial connection
connectWithRetry();
// Initialize scheduled tasks
function initializeScheduledTasks() {
// Import backup script
const { performBackup } = require('./scripts/backup');
// Schedule daily backup at 3:00 AM
const backupJob = new CronJob('0 3 * * *', performBackup, null, true);
console.log('Scheduled automatic database backup job');
// Schedule weekly database maintenance at 2:00 AM on Sundays
const maintenanceJob = new CronJob('0 2 * * 0', async () => {
console.log('Running weekly database maintenance...');
try {
// Perform any maintenance tasks here
// For example, compact collections, validate data integrity, etc.
console.log('Database maintenance completed successfully');
} catch (error) {
console.error('Database maintenance error:', error);
}
}, null, true);
console.log('Scheduled weekly database maintenance job');
}
// Health check endpoint
app.get('/api/health', (req, res) => {
const dbStatus = mongoose.connection.readyState === 1 ? 'connected' : 'disconnected';
res.json({
status: 'ok',
timestamp: new Date().toISOString(),
server: {
uptime: process.uptime(),
memory: process.memoryUsage(),
},
database: {
status: dbStatus
}
});
});
// API Routes
app.use('/api/words', wordRoutes);
app.use('/api/datasets', datasetRoutes);
// Serve the main HTML file for any other request
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '..', 'index.html'));
});
// Error handling middleware
app.use((err, req, res, next) => {
console.error('Unhandled error:', err);
res.status(500).json({ error: 'Internal server error', message: err.message });
});
// Handle uncaught exceptions
process.on('uncaughtException', (err) => {
console.error('Uncaught exception:', err);
// Keep the process alive but log the error
});
// Handle unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled promise rejection:', reason);
// Keep the process alive but log the error
});
// Start the server
const server = app.listen(PORT, () => {
console.log(`R00TS server running on port ${PORT}`);
console.log(`Server time: ${new Date().toLocaleString()}`);
console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
console.log(`MongoDB URI: ${process.env.MONGODB_URI.replace(/\/.*@/, '//***:***@')}`);
console.log('Server is ready to accept connections');
});
// Graceful shutdown
process.on('SIGTERM', () => {
console.log('SIGTERM received, shutting down gracefully');
server.close(() => {
console.log('Server closed');
mongoose.connection.close(false, () => {
console.log('MongoDB connection closed');
process.exit(0);
});
});
});
module.exports = app; // Export for testing