Spaces:
Running
Running
merasabkuch
commited on
Commit
•
5774ae9
1
Parent(s):
64cb18f
Upload 3 files
Browse files- Dockerfile +32 -0
- index.js +188 -0
- package.json +19 -0
Dockerfile
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM node:18-slim
|
2 |
+
|
3 |
+
# Switch to the node user
|
4 |
+
USER node
|
5 |
+
|
6 |
+
# Set environment variables for the user
|
7 |
+
ENV HOME=/home/node \
|
8 |
+
PATH=/home/node/.local/bin:$PATH
|
9 |
+
|
10 |
+
# Set the working directory
|
11 |
+
WORKDIR $HOME/app
|
12 |
+
|
13 |
+
# Copy the package.json and package-lock.json files to the working directory
|
14 |
+
COPY --chown=node:node package*.json ./
|
15 |
+
|
16 |
+
# Install Node.js dependencies
|
17 |
+
RUN npm install
|
18 |
+
|
19 |
+
# Copy the application code to the working directory
|
20 |
+
COPY --chown=node:node . .
|
21 |
+
|
22 |
+
# Ensure the ownership of the directory to the node user
|
23 |
+
RUN chown -R node:node .
|
24 |
+
|
25 |
+
# Expose the port the app runs on
|
26 |
+
EXPOSE 7860
|
27 |
+
|
28 |
+
RUN echo $SERVICE_ACC_JSON > serviceAcc.json
|
29 |
+
|
30 |
+
|
31 |
+
# Command to run the Node.js server
|
32 |
+
CMD ["node", "index.js"]
|
index.js
ADDED
@@ -0,0 +1,188 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// server.js (Node.js with Express and ws)
|
2 |
+
|
3 |
+
const express = require('express');
|
4 |
+
const { WebSocketServer } = require('ws');
|
5 |
+
const cors = require('cors'); // Import CORS
|
6 |
+
|
7 |
+
const { initializeApp, cert } = require('firebase-admin/app');
|
8 |
+
const { getFirestore } = require('firebase-admin/firestore');
|
9 |
+
|
10 |
+
var admin = require("firebase-admin");
|
11 |
+
|
12 |
+
const serviceAccount = require("./serviceAcc.json");
|
13 |
+
|
14 |
+
initializeApp({
|
15 |
+
credential: cert(serviceAccount)
|
16 |
+
});
|
17 |
+
|
18 |
+
|
19 |
+
const db = getFirestore();
|
20 |
+
const app = express();
|
21 |
+
const port = process.env.PORT || 8080;
|
22 |
+
|
23 |
+
const wss = new WebSocketServer({ noServer: true });
|
24 |
+
|
25 |
+
app.use(cors());
|
26 |
+
|
27 |
+
var initialData = {};
|
28 |
+
let currentDeviceStates = {};
|
29 |
+
|
30 |
+
const fetchInitialData = async () => {
|
31 |
+
const devices = [];
|
32 |
+
const rooms = [];
|
33 |
+
const users = [];
|
34 |
+
|
35 |
+
try {
|
36 |
+
const deviceSnapshot = await db.collection('Devices').get();
|
37 |
+
deviceSnapshot.forEach(doc => devices.push({ id: doc.id, ...doc.data() }));
|
38 |
+
|
39 |
+
const roomsSnapshot = await db.collectionGroup('Rooms').get(); // Get all rooms across hostels
|
40 |
+
roomsSnapshot.forEach(doc => rooms.push({ id: doc.id, ...doc.data(), hostelId: doc.ref.parent.parent.id })); // Include hostelId
|
41 |
+
|
42 |
+
const usersSnapshot = await db.collection('Users').get();
|
43 |
+
usersSnapshot.forEach(doc => users.push({ id: doc.id, ...doc.data() }));
|
44 |
+
|
45 |
+
initialData = { devices, rooms, users };
|
46 |
+
|
47 |
+
// console.log("Initial data fetched:", initialData);
|
48 |
+
//for each hostel id mantain a list of rooms with devices pertaining to that hostel
|
49 |
+
|
50 |
+
//setall to off
|
51 |
+
|
52 |
+
initialData.devices.forEach(device => {
|
53 |
+
device.state = false;
|
54 |
+
} );
|
55 |
+
|
56 |
+
initialData.rooms.forEach(room => {
|
57 |
+
if (!currentDeviceStates[room.id]) {
|
58 |
+
currentDeviceStates[room.id] = [];
|
59 |
+
}
|
60 |
+
|
61 |
+
const roomDevices = initialData.devices.filter(device => device.hostelId === room.hostelId);
|
62 |
+
currentDeviceStates[room.id] = { ...room, devices: roomDevices };
|
63 |
+
});
|
64 |
+
|
65 |
+
|
66 |
+
|
67 |
+
|
68 |
+
} catch (error) {
|
69 |
+
console.error("Error fetching initial data:", error);
|
70 |
+
// return { devices: [], rooms: [], users: [] };
|
71 |
+
initialData = { devices, rooms, users };
|
72 |
+
}
|
73 |
+
};
|
74 |
+
|
75 |
+
|
76 |
+
|
77 |
+
fetchInitialData();
|
78 |
+
|
79 |
+
|
80 |
+
|
81 |
+
const connectedClients = {}; // Store connected clients per room
|
82 |
+
|
83 |
+
wss.on('connection', (ws) => {
|
84 |
+
ws.on('message', async (message) => {
|
85 |
+
try {
|
86 |
+
const parsedMessage = JSON.parse(message);
|
87 |
+
const { roomId, userId, apiKey } = parsedMessage;
|
88 |
+
|
89 |
+
// Helper function to check authorization
|
90 |
+
const isAuthorized = (roomId, userId, apiKey) => {
|
91 |
+
return initialData.users.some(user => user.id === userId && user.roomId === roomId) || apiKey === "masterPassword@tiet@123";
|
92 |
+
};
|
93 |
+
|
94 |
+
switch (parsedMessage.type) {
|
95 |
+
case 'joinRoom':
|
96 |
+
if (!roomId || !userId) {
|
97 |
+
ws.send(JSON.stringify({ error: 'Invalid room or user ID' }));
|
98 |
+
return;
|
99 |
+
}
|
100 |
+
|
101 |
+
if (!isAuthorized(roomId, userId, apiKey)) {
|
102 |
+
console.log("Unauthorized joinRoom attempt.");
|
103 |
+
ws.send(JSON.stringify({ error: 'Unauthorized' }));
|
104 |
+
return;
|
105 |
+
}
|
106 |
+
|
107 |
+
// Store the connected client
|
108 |
+
if (!connectedClients[roomId]) {
|
109 |
+
connectedClients[roomId] = [];
|
110 |
+
}
|
111 |
+
connectedClients[roomId].push(ws);
|
112 |
+
|
113 |
+
|
114 |
+
ws.send(JSON.stringify({ type: 'roomData', roomData: currentDeviceStates[roomId], roomId: roomId }));
|
115 |
+
break;
|
116 |
+
|
117 |
+
case 'updateDeviceState':
|
118 |
+
const { deviceId, state } = parsedMessage;
|
119 |
+
if (!deviceId || state === undefined || !roomId || (!userId && !apiKey)) { // Check for undefined state or missing userId/apiKey
|
120 |
+
ws.send(JSON.stringify({ error: 'Invalid device ID, state, room ID, user ID, or API key' }));
|
121 |
+
return;
|
122 |
+
}
|
123 |
+
|
124 |
+
if (!isAuthorized(roomId, userId, apiKey)) {
|
125 |
+
console.log("Unauthorized updateDeviceState attempt.");
|
126 |
+
ws.send(JSON.stringify({ error: 'Unauthorized' }));
|
127 |
+
return;
|
128 |
+
}
|
129 |
+
|
130 |
+
|
131 |
+
const room = currentDeviceStates[roomId];
|
132 |
+
if (room) { // Check if the room exists
|
133 |
+
const device = room.devices.find(dev => dev.id === deviceId);
|
134 |
+
if (device) { // Check if the device exists
|
135 |
+
device.state = state;
|
136 |
+
|
137 |
+
// Broadcast to all clients in the room
|
138 |
+
if (connectedClients[roomId]) {
|
139 |
+
connectedClients[roomId].forEach(client => {
|
140 |
+
client.send(JSON.stringify({ type: 'deviceUpdate', deviceId, state, roomId: roomId }));
|
141 |
+
});
|
142 |
+
}
|
143 |
+
|
144 |
+
|
145 |
+
} else {
|
146 |
+
ws.send(JSON.stringify({ error: 'Device not found' }));
|
147 |
+
}
|
148 |
+
} else {
|
149 |
+
ws.send(JSON.stringify({ error: 'Room not found' }));
|
150 |
+
}
|
151 |
+
|
152 |
+
break;
|
153 |
+
|
154 |
+
default:
|
155 |
+
console.log('Unknown message type:', parsedMessage.type);
|
156 |
+
}
|
157 |
+
|
158 |
+
} catch (error) {
|
159 |
+
console.error('Error handling message:', error);
|
160 |
+
ws.send(JSON.stringify({ error: 'Invalid message format' })); // Send error back to client
|
161 |
+
}
|
162 |
+
});
|
163 |
+
|
164 |
+
ws.on('close', () => {
|
165 |
+
for (const roomId in connectedClients) {
|
166 |
+
connectedClients[roomId] = connectedClients[roomId].filter(client => client !== ws);
|
167 |
+
}
|
168 |
+
});
|
169 |
+
|
170 |
+
ws.on('error', (error) => {
|
171 |
+
console.error('WebSocket error:', error);
|
172 |
+
});
|
173 |
+
});
|
174 |
+
|
175 |
+
|
176 |
+
console.log('WebSocket server started on port 8080');
|
177 |
+
|
178 |
+
|
179 |
+
|
180 |
+
|
181 |
+
|
182 |
+
const server = app.listen(port, () => console.log(`Listening on port ${port}`));
|
183 |
+
|
184 |
+
server.on('upgrade', (request, socket, head) => {
|
185 |
+
wss.handleUpgrade(request, socket, head, ws => {
|
186 |
+
wss.emit('connection', ws, request);
|
187 |
+
});
|
188 |
+
});
|
package.json
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "room_sync_sockets",
|
3 |
+
"version": "1.0.0",
|
4 |
+
"main": "index.js",
|
5 |
+
"scripts": {
|
6 |
+
"start": "node index.js",
|
7 |
+
"test": "echo \"Error: no test specified\" && exit 1"
|
8 |
+
},
|
9 |
+
"keywords": [],
|
10 |
+
"author": "",
|
11 |
+
"license": "ISC",
|
12 |
+
"description": "",
|
13 |
+
"dependencies": {
|
14 |
+
"cors": "^2.8.5",
|
15 |
+
"express": "^4.21.1",
|
16 |
+
"firebase-admin": "^12.6.0",
|
17 |
+
"ws": "^8.18.0"
|
18 |
+
}
|
19 |
+
}
|