![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/domains/lavocat.quebec/public_html/ |
// Production server configuration for lavocat.quebec
require('dotenv').config({ path: '.env.production' });
const { createServer } = require('https');
const { createServer: createHttpServer } = require('http');
const { parse } = require('url');
const fs = require('fs');
const path = require('path');
const next = require('next');
const WebSocket = require('ws');
const os = require('os');
const dev = false; // Production mode
const hostname = process.env.HOSTNAME || '0.0.0.0';
const port = process.env.PORT || 3000;
const httpsPort = process.env.HTTPS_PORT || 3443;
// Production Next.js app
const app = next({
dev,
hostname,
port,
conf: require('./next.config.production.js')
});
const handle = app.getRequestHandler();
// Global variables for WebSocket management
global.wsClients = new Map();
global.wsRoomSubscriptions = new Map();
global.wsUserPresence = new Map();
// Memory management for production
const MEMORY_CLEANUP_INTERVAL = 5 * 60 * 1000; // 5 minutes
const MAX_WS_CLIENTS = 1000;
const MAX_WS_ROOMS = 500;
// Periodic memory cleanup
setInterval(() => {
try {
// Clean up disconnected WebSocket clients
for (const [clientId, client] of global.wsClients) {
if (client.readyState === WebSocket.CLOSED || client.readyState === WebSocket.CLOSING) {
global.wsClients.delete(clientId);
console.log(`๐งน Cleaned up disconnected WebSocket client: ${clientId}`);
}
}
// Clean up empty rooms
for (const [roomId, subscribers] of global.wsRoomSubscriptions) {
if (subscribers.size === 0) {
global.wsRoomSubscriptions.delete(roomId);
console.log(`๐งน Cleaned up empty room: ${roomId}`);
}
}
// Force garbage collection if available
if (global.gc) {
global.gc();
}
console.log(`๐ Memory status - Clients: ${global.wsClients.size}, Rooms: ${global.wsRoomSubscriptions.size}`);
} catch (error) {
console.error('Memory cleanup error:', error);
}
}, MEMORY_CLEANUP_INTERVAL);
// Helper function to get network IP
const getNetworkIP = () => {
const interfaces = os.networkInterfaces();
for (const name of Object.keys(interfaces)) {
for (const iface of interfaces[name]) {
if (iface.family === 'IPv4' && !iface.internal) {
return iface.address;
}
}
}
return 'localhost';
};
// HTTPS configuration for production
const getHttpsOptions = () => {
try {
const keyPath = process.env.SSL_KEY_PATH || path.join(__dirname, 'certificates', 'lavocat.quebec.key');
const certPath = process.env.SSL_CERT_PATH || path.join(__dirname, 'certificates', 'lavocat.quebec.crt');
if (fs.existsSync(keyPath) && fs.existsSync(certPath)) {
console.log('๐ Using production SSL certificates');
return {
key: fs.readFileSync(keyPath),
cert: fs.readFileSync(certPath)
};
}
} catch (error) {
console.warn('Could not load HTTPS certificates:', error.message);
}
return null;
};
function initializeWebSocketServer(server) {
if (global.wsServer) {
console.log('WebSocket server is already running.');
return;
}
const wss = new WebSocket.Server({
server,
path: '/_ws',
clientTracking: true,
maxPayload: 1024 * 1024 // 1MB max payload
});
global.wsServer = wss;
const clients = global.wsClients;
const userPresence = global.wsUserPresence;
console.log('๐ WebSocket server initialized for production');
// WebSocket connection handling
wss.on('connection', (ws, req) => {
const url = new URL(req.url, `http://${req.headers.host}`);
const token = url.searchParams.get('token');
let userId, user, connId;
if (token) {
try {
const decoded = JSON.parse(Buffer.from(token, 'base64').toString());
userId = decoded.userId;
connId = decoded.connId || 'unknown';
user = {
id: decoded.userId,
name: decoded.name,
};
// Token age validation
const tokenAge = Date.now() - decoded.timestamp;
if (tokenAge > 300000) { // 5 minutes max
console.log(`๐จ SECURITY: Token expired (age: ${tokenAge}ms)`);
ws.close(1008, 'Token expired');
return;
}
console.log(`โ
Secure token authentication for user: ${user.name} (${userId}) [${connId}]`);
} catch (error) {
console.log(`๐จ SECURITY: Invalid token format:`, error.message);
ws.close(1008, 'Invalid token format');
return;
}
} else {
console.log(`๐จ SECURITY: No authentication provided`);
ws.close(1008, 'Authentication required');
return;
}
if (!userId || !user.id) {
console.log(`๐จ SECURITY: Connection rejected - invalid user data`);
ws.close(1008, 'Invalid user data');
return;
}
console.log(`โ
WebSocket connection established for user: ${user.name} (${userId}) [${connId}]`);
// MEMORY MANAGEMENT: Check connection limits
if (global.wsClients.size >= MAX_WS_CLIENTS) {
console.log(`๐จ MEMORY: Rejecting connection - too many clients (${global.wsClients.size}/${MAX_WS_CLIENTS})`);
ws.close(1013, 'Server overloaded');
return;
}
// Rate limiting per connection
const rateLimiter = {
messages: 0,
lastReset: Date.now(),
maxMessages: 100,
windowMs: 60000
};
// Set user presence
userPresence.set(userId, {
status: 'online',
lastSeen: Date.now(),
currentRoom: null
});
const clientInfo = { userId, user, rooms: new Set(), isAlive: true, connId, rateLimiter };
clients.set(ws, clientInfo);
ws.clientInfo = clientInfo;
// Heartbeat mechanism
ws.isAlive = true;
ws.on('pong', () => {
ws.isAlive = true;
});
// Message handling
ws.on('message', (data) => {
try {
const message = JSON.parse(data.toString());
// Rate limiting
const now = Date.now();
if (now - rateLimiter.lastReset > rateLimiter.windowMs) {
rateLimiter.messages = 0;
rateLimiter.lastReset = now;
}
if (rateLimiter.messages >= rateLimiter.maxMessages) {
console.log(`๐จ RATE LIMIT: User ${user.name} exceeded message limit`);
ws.close(1008, 'Rate limit exceeded');
return;
}
rateLimiter.messages++;
// Handle different message types
switch (message.type) {
case 'ping':
ws.send(JSON.stringify({ type: 'pong', timestamp: Date.now() }));
break;
case 'join_room':
// Handle room joining
break;
case 'leave_room':
// Handle room leaving
break;
default:
// Broadcast to room or handle other message types
break;
}
} catch (error) {
console.error('WebSocket message error:', error);
}
});
// Connection close handling
ws.on('close', (code, reason) => {
console.log(`๐ WebSocket connection closed for user ${user.name} [${connId}] - Code: ${code}, Reason: ${reason}`);
// Update user presence
userPresence.set(userId, {
status: 'offline',
lastSeen: Date.now(),
currentRoom: null
});
// Clean up client
clients.delete(ws);
// performanceMonitor.trackWebSocketDisconnection(); // This line was not in the original file, so it's removed.
});
// Error handling
ws.on('error', (error) => {
console.error(`โ WebSocket error for user ${user.name}:`, error);
clients.delete(ws);
});
});
// Heartbeat interval
const interval = setInterval(() => {
wss.clients.forEach((ws) => {
if (ws.isAlive === false) {
return ws.terminate();
}
ws.isAlive = false;
ws.ping();
});
}, 30000);
wss.on('close', () => {
clearInterval(interval);
});
}
// Start the application
app.prepare().then(() => {
const httpsOptions = getHttpsOptions();
if (httpsOptions) {
// HTTPS Server
const httpsServer = createServer(httpsOptions, (req, res) => {
const parsedUrl = parse(req.url, true);
handle(req, res, parsedUrl);
});
initializeWebSocketServer(httpsServer);
httpsServer.listen(httpsPort, hostname, (err) => {
if (err) throw err;
console.log(`๐ Production HTTPS server running on https://${hostname}:${httpsPort}`);
console.log(`๐ Domain: lavocat.quebec`);
console.log(`๐ Environment: Production`);
});
} else {
// HTTP Server (fallback)
const httpServer = createHttpServer((req, res) => {
const parsedUrl = parse(req.url, true);
handle(req, res, parsedUrl);
});
initializeWebSocketServer(httpServer);
httpServer.listen(port, hostname, (err) => {
if (err) throw err;
console.log(`๐ Production HTTP server running on http://${hostname}:${port}`);
console.log(`๐ Domain: lavocat.quebec`);
console.log(`๐ Environment: Production`);
});
}
}).catch((err) => {
console.error('โ Error starting production server:', err);
process.exit(1);
});
// Graceful shutdown
process.on('SIGTERM', () => {
console.log('๐ SIGTERM received, shutting down gracefully');
if (global.wsServer) {
global.wsServer.close();
}
process.exit(0);
});
process.on('SIGINT', () => {
console.log('๐ SIGINT received, shutting down gracefully');
if (global.wsServer) {
global.wsServer.close();
}
process.exit(0);
});