-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
203 lines (171 loc) · 5.82 KB
/
server.js
File metadata and controls
203 lines (171 loc) · 5.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
const express = require('express');
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const { Server } = require('socket.io');
const http = require('http');
const app = express();
const server = http.createServer(app);
const io = new Server(server);
// Create uploads directory if it doesn't exist
const uploadsDir = path.join(__dirname, 'uploads');
if (!fs.existsSync(uploadsDir)) {
fs.mkdirSync(uploadsDir);
}
// Configure multer for image uploads
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/');
},
filename: (req, file, cb) => {
const timestamp = Date.now();
cb(null, `photo-${timestamp}.png`);
}
});
const upload = multer({ storage });
// Force HTTPS in production (when using ngrok or similar)
app.use((req, res, next) => {
if (req.header('x-forwarded-proto') !== 'https' && process.env.NODE_ENV === 'production') {
res.redirect(`https://${req.header('host')}${req.url}`);
} else {
next();
}
});
// Serve static files
app.use(express.static('public'));
app.use('/uploads', express.static('uploads'));
// Routes
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
app.get('/admin', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'admin.html'));
});
// API endpoint to upload photos
app.post('/upload-photo', upload.single('photo'), (req, res) => {
if (!req.file) {
return res.status(400).json({ error: 'No photo uploaded' });
}
const photoData = {
filename: req.file.filename,
timestamp: new Date().toISOString(),
size: req.file.size
};
// Notify admin via WebSocket
io.emit('new-photo', photoData);
res.json({ success: true, photo: photoData });
});
// API endpoint to get all photos
app.get('/api/photos', (req, res) => {
fs.readdir(uploadsDir, (err, files) => {
if (err) {
return res.status(500).json({ error: 'Failed to read photos' });
}
const photos = files
.filter(file => file.endsWith('.png'))
.map(file => {
const stats = fs.statSync(path.join(uploadsDir, file));
return {
filename: file,
timestamp: stats.birthtime.toISOString(),
size: stats.size
};
})
.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
res.json(photos);
});
});
// WebSocket connection handling
const connectedClients = new Map();
const connectedAdmins = new Map();
io.on('connection', (socket) => {
console.log('User connected:', socket.id);
// Handle client (camera) connections
socket.on('client-ready', () => {
console.log('Camera client ready:', socket.id);
connectedClients.set(socket.id, socket);
// Notify all admins that a new camera is available
connectedAdmins.forEach(adminSocket => {
adminSocket.emit('camera-available', { clientId: socket.id });
});
});
// Handle admin connections
socket.on('admin-ready', () => {
console.log('Admin connected:', socket.id);
connectedAdmins.set(socket.id, socket);
// Send list of available cameras to this admin
const availableCameras = Array.from(connectedClients.keys());
socket.emit('available-cameras', availableCameras);
});
// WebRTC signaling
socket.on('offer', (data) => {
console.log('Server: Received offer from', socket.id, 'for target', data.target);
const targetSocket = connectedAdmins.get(data.target) || connectedClients.get(data.target);
if (targetSocket) {
console.log('Server: Forwarding offer to target', data.target);
targetSocket.emit('offer', {
offer: data.offer,
sender: socket.id
});
} else {
console.log('Server: Target socket not found:', data.target);
}
});
socket.on('answer', (data) => {
console.log('Server: Received answer from', socket.id, 'for target', data.target);
const targetSocket = connectedAdmins.get(data.target) || connectedClients.get(data.target);
if (targetSocket) {
console.log('Server: Forwarding answer to target', data.target);
targetSocket.emit('answer', {
answer: data.answer,
sender: socket.id
});
} else {
console.log('Server: Target socket not found for answer:', data.target);
}
});
socket.on('ice-candidate', (data) => {
const targetSocket = connectedAdmins.get(data.target) || connectedClients.get(data.target);
if (targetSocket) {
targetSocket.emit('ice-candidate', {
candidate: data.candidate,
sender: socket.id
});
}
});
// Handle client stopping camera
socket.on('client-stopped', () => {
console.log('Client stopped camera:', socket.id);
connectedClients.delete(socket.id);
// Notify all admins that camera stopped
connectedAdmins.forEach(adminSocket => {
adminSocket.emit('camera-disconnected', { clientId: socket.id });
});
});
// Handle camera switch notification
socket.on('camera-switched', (data) => {
console.log('Client switched camera:', socket.id, 'to:', data.cameraName);
// Notify all admins about camera switch
connectedAdmins.forEach(adminSocket => {
adminSocket.emit('camera-switched', {
clientId: socket.id,
cameraName: data.cameraName
});
});
});
socket.on('disconnect', () => {
console.log('User disconnected:', socket.id);
// Remove from both maps
connectedClients.delete(socket.id);
connectedAdmins.delete(socket.id);
// Notify admins if a camera disconnected
connectedAdmins.forEach(adminSocket => {
adminSocket.emit('camera-disconnected', { clientId: socket.id });
});
});
});
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
console.log(`Admin panel: http://localhost:${PORT}/admin`);
});