// server.js (Node.js with Express and ws) const express = require('express'); const { WebSocketServer } = require('ws'); const cors = require('cors'); // Import CORS const { initializeApp, cert } = require('firebase-admin/app'); const { getFirestore } = require('firebase-admin/firestore'); var admin = require("firebase-admin"); const serviceAccount = require("./serviceAcc.json"); initializeApp({ credential: cert(serviceAccount) }); const db = getFirestore(); const app = express(); const port = process.env.PORT || 8080; const wss = new WebSocketServer({ noServer: true }); app.use(cors()); var initialData = {}; let currentDeviceStates = {}; const fetchInitialData = async () => { const devices = []; const rooms = []; const users = []; try { const deviceSnapshot = await db.collection('Devices').get(); deviceSnapshot.forEach(doc => devices.push({ id: doc.id, ...doc.data() })); const roomsSnapshot = await db.collectionGroup('Rooms').get(); // Get all rooms across hostels roomsSnapshot.forEach(doc => rooms.push({ id: doc.id, ...doc.data(), hostelId: doc.ref.parent.parent.id })); // Include hostelId const usersSnapshot = await db.collection('Users').get(); usersSnapshot.forEach(doc => users.push({ id: doc.id, ...doc.data() })); initialData = { devices, rooms, users }; // console.log("Initial data fetched:", initialData); //for each hostel id mantain a list of rooms with devices pertaining to that hostel //setall to off initialData.devices.forEach(device => { device.state = false; } ); initialData.rooms.forEach(room => { if (!currentDeviceStates[room.id]) { currentDeviceStates[room.id] = []; } const roomDevices = initialData.devices.filter(device => device.hostelId === room.hostelId); currentDeviceStates[room.id] = { ...room, devices: roomDevices }; }); } catch (error) { console.error("Error fetching initial data:", error); // return { devices: [], rooms: [], users: [] }; initialData = { devices, rooms, users }; } }; fetchInitialData(); const connectedClients = {}; // Store connected clients per room wss.on('connection', (ws) => { ws.on('message', async (message) => { try { const parsedMessage = JSON.parse(message); const { roomId, userId, apiKey } = parsedMessage; // Helper function to check authorization const isAuthorized = (roomId, userId, apiKey) => { return initialData.users.some(user => user.id === userId && user.roomId === roomId) || apiKey === "masterPassword@tiet@123"; }; switch (parsedMessage.type) { case 'joinRoom': if (!roomId || !userId) { ws.send(JSON.stringify({ error: 'Invalid room or user ID' })); return; } if (!isAuthorized(roomId, userId, apiKey)) { console.log("Unauthorized joinRoom attempt."); ws.send(JSON.stringify({ error: 'Unauthorized' })); return; } // Store the connected client if (!connectedClients[roomId]) { connectedClients[roomId] = []; } connectedClients[roomId].push(ws); ws.send(JSON.stringify({ type: 'roomData', roomData: currentDeviceStates[roomId], roomId: roomId })); break; case 'updateDeviceState': const { deviceId, state } = parsedMessage; if (!deviceId || state === undefined || !roomId || (!userId && !apiKey)) { // Check for undefined state or missing userId/apiKey ws.send(JSON.stringify({ error: 'Invalid device ID, state, room ID, user ID, or API key' })); return; } if (!isAuthorized(roomId, userId, apiKey)) { console.log("Unauthorized updateDeviceState attempt."); ws.send(JSON.stringify({ error: 'Unauthorized' })); return; } const room = currentDeviceStates[roomId]; if (room) { // Check if the room exists const device = room.devices.find(dev => dev.id === deviceId); if (device) { // Check if the device exists device.state = state; // Broadcast to all clients in the room if (connectedClients[roomId]) { connectedClients[roomId].forEach(client => { client.send(JSON.stringify({ type: 'deviceUpdate', deviceId, state, roomId: roomId })); }); } } else { ws.send(JSON.stringify({ error: 'Device not found' })); } } else { ws.send(JSON.stringify({ error: 'Room not found' })); } break; default: console.log('Unknown message type:', parsedMessage.type); } } catch (error) { console.error('Error handling message:', error); ws.send(JSON.stringify({ error: 'Invalid message format' })); // Send error back to client } }); ws.on('close', () => { for (const roomId in connectedClients) { connectedClients[roomId] = connectedClients[roomId].filter(client => client !== ws); } }); ws.on('error', (error) => { console.error('WebSocket error:', error); }); }); console.log('WebSocket server started on port 8080'); const server = app.listen(port, () => console.log(`Listening on port ${port}`)); server.on('upgrade', (request, socket, head) => { wss.handleUpgrade(request, socket, head, ws => { wss.emit('connection', ws, request); }); });