Skip to content

Commit ca68d50

Browse files
committed
Camera stream fix
1 parent 94040a1 commit ca68d50

3 files changed

Lines changed: 48 additions & 7 deletions

File tree

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { IoAdapter } from '@nestjs/platform-socket.io';
2+
import { INestApplication } from '@nestjs/common';
3+
import { ServerOptions } from 'socket.io';
4+
5+
/**
6+
* Custom Socket.IO v4 adapter.
7+
*
8+
* Centralises server-level options (CORS, transports, EIO3 compat) here
9+
* rather than spreading them across every @WebSocketGateway decorator.
10+
*/
11+
export class SocketIoAdapter extends IoAdapter {
12+
constructor(private readonly app: INestApplication) {
13+
super(app);
14+
}
15+
16+
createIOServer(port: number, options?: ServerOptions) {
17+
const corsOrigins = process.env.CORS_ORIGIN?.split(',') ?? ['*'];
18+
19+
return super.createIOServer(port, {
20+
...options,
21+
cors: {
22+
origin: corsOrigins,
23+
methods: ['GET', 'POST'],
24+
credentials: true,
25+
},
26+
transports: ['websocket', 'polling'],
27+
allowEIO3: true, // Engine.IO v3 compat (python-socketio clients)
28+
} satisfies ServerOptions);
29+
}
30+
}

backend/src/endpoints/camera/camera.gateway.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ import {
1010
import { Server, Socket } from 'socket.io';
1111

1212
@WebSocketGateway({
13-
namespace: '/camera', // must match PUSH_NAMESPACE (default: /camera)
14-
cors: { origin: '*' }, // restrict in production
15-
transports: ['websocket', 'polling'], // allow python-socketio polling handshake
16-
allowEIO3: true, // accept Engine.IO v3 clients (python-socketio compat)
13+
namespace: '/camera', // must match PUSH_NAMESPACE (default: /camera)
14+
// cors / transports / allowEIO3 are set globally in SocketIoAdapter
1715
})
1816
export class CameraGateway
1917
implements OnGatewayConnection, OnGatewayDisconnect
@@ -35,6 +33,10 @@ export class CameraGateway
3533
secret?: string;
3634
};
3735

36+
console.log(
37+
`[CameraGateway] handleConnection — id=${client.id} deviceId=${deviceId ?? '(none)'} transport=${client.conn.transport.name}`,
38+
);
39+
3840
// Only edge-gateway connections supply a deviceId.
3941
// Browser consumers connect without auth — let them through.
4042
if (!deviceId) {
@@ -44,6 +46,9 @@ export class CameraGateway
4446
// Validate shared secret for gateway connections
4547
const expected = process.env.CAMERA_PUSH_SECRET ?? '';
4648
if (expected && secret !== expected) {
49+
console.warn(
50+
`[CameraGateway] rejected ${deviceId}: secret mismatch (expected=${expected ? '<set>' : '<empty>'} received=${secret ? '<set>' : '<empty>'})`,
51+
);
4752
client.disconnect(true);
4853
return;
4954
}
@@ -55,7 +60,7 @@ export class CameraGateway
5560
this.deviceCameras.set(deviceId, new Set());
5661
}
5762

58-
console.log(`[CameraGateway] gateway connected: ${deviceId}`);
63+
console.log(`[CameraGateway] gateway accepted: ${deviceId}`);
5964
}
6065

6166
handleDisconnect(client: Socket) {
@@ -76,8 +81,7 @@ export class CameraGateway
7681
jpeg: Buffer; // raw JPEG bytes
7782
},
7883
@ConnectedSocket() _client: Socket,
79-
) {
80-
const key = `${data.deviceId}:${data.cameraIndex}`;
84+
) { const key = `${data.deviceId}:${data.cameraIndex}`;
8185
this.frames.set(key, data.jpeg);
8286

8387
// Track this camera index for the device

backend/src/main.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,19 @@
1616

1717
import { NestFactory } from '@nestjs/core';
1818
import { AppModule } from './app.module';
19+
import { SocketIoAdapter } from './adapters/socket-io.adapter';
1920
import * as cors from 'cors';
2021
import * as cookieParser from 'cookie-parser';
2122
import * as session from 'express-session';
2223
import * as dotenv from 'dotenv';
2324
import { AllExceptionsFilter } from './utils/exception.middleware';
25+
import * as bodyParser from 'body-parser';
2426

2527
dotenv.config();
2628

2729
async function bootstrap() {
2830
const app = await NestFactory.create(AppModule);
31+
app.useWebSocketAdapter(new SocketIoAdapter(app));
2932
const allowedOrigins = process.env.CORS_ORIGIN?.split(',') || [];
3033
app.useGlobalFilters(new AllExceptionsFilter());
3134
app.enableCors({
@@ -42,6 +45,10 @@ async function bootstrap() {
4245
saveUninitialized: false,
4346
}),
4447
);
48+
49+
app.use(bodyParser.json({ limit: '100mb' }));
50+
app.use(bodyParser.urlencoded({ limit: '100mb', extended: true }));
51+
4552
await app.listen(4002);
4653
}
4754
bootstrap();

0 commit comments

Comments
 (0)