Skip to content

Commit e6fa5c2

Browse files
committed
Fix CORS handling, SSE users, and admin route cleanup
1 parent 6b1fd3c commit e6fa5c2

8 files changed

Lines changed: 27 additions & 316 deletions

File tree

backend/.env.example

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,3 @@ ADMIN_PUBLIC_KEY=
4545
# Leave empty to run in single-instance mode (no Redis required).
4646
REDIS_URL=
4747

48-
# ─── Admin ───────────────────────────────────────────────────────────────────
49-
# Bearer token required for GET /v1/admin/metrics.
50-
# Leave empty to disable the admin endpoint entirely.
51-
ADMIN_SECRET=

backend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"type": "module",
66
"main": "index.js",
77
"scripts": {
8+
"prebuild": "prisma generate",
89
"test": "vitest run",
910
"dev": "nodemon",
1011
"build": "tsc",

backend/src/app.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ app.use(cors({
6565
}));
6666

6767
// Convert CORS errors into 403 responses so callers get a clear status code
68-
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
69-
if (err && err.message === 'CORS origin not allowed') {
68+
app.use((err: unknown, req: Request, res: Response, next: NextFunction) => {
69+
if (err instanceof Error && err.message === 'CORS origin not allowed') {
7070
res.status(403).json({ error: 'CORS origin not allowed' });
7171
return;
7272
}

backend/src/controllers/sse.controller.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { z } from 'zod';
77

88
const subscribeSchema = z.object({
99
streams: z.array(z.string()).optional().default([]),
10+
users: z.array(z.string()).optional().default([]),
1011
all: z.boolean().optional().default(false),
1112
});
1213

@@ -41,7 +42,7 @@ export const subscribe = async (req: Request, res: Response) => {
4142
}
4243

4344
const { publicKey } = (req as AuthenticatedRequest).user;
44-
const { streams, all } = subscribeSchema.parse(req.query);
45+
const { streams, users, all } = subscribeSchema.parse(req.query);
4546

4647
// Scope: only streams where the authenticated user is sender or recipient
4748
const ownedStreams = await prisma.stream.findMany({
@@ -61,6 +62,8 @@ export const subscribe = async (req: Request, res: Response) => {
6162
subscriptions = [...ownedIds] as string[];
6263
}
6364

65+
subscriptions.push(...users.map((userKey) => `user:${userKey}`));
66+
6467
// Always add user-scoped subscription key
6568
subscriptions.push(`user:${publicKey}`);
6669

@@ -77,11 +80,11 @@ export const subscribe = async (req: Request, res: Response) => {
7780
res.write(`data: ${JSON.stringify({ type: 'connected', clientId, requestId })}\n\n`);
7881

7982
sseService.addClient(clientId, res, subscriptions, sourceIp);
80-
} catch (error: any) {
81-
if (error.name === 'ZodError') {
83+
} catch (error: unknown) {
84+
if (error instanceof z.ZodError) {
8285
return res.status(400).json({
8386
message: 'Invalid subscription parameters',
84-
errors: error.errors,
87+
errors: error.issues,
8588
});
8689
}
8790
return res.status(500).json({ message: 'Internal server error' });

backend/src/routes/adminRoutes.ts

Lines changed: 0 additions & 142 deletions
This file was deleted.

backend/src/routes/v1/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import eventsRoutes from './events.routes.js';
44
import userRoutes from './user.routes.js';
55
import authRoutes from './auth.routes.js';
66
import adminRoutes from './admin.routes.js';
7-
import adminMetricsRoutes from '../adminRoutes.js';
87

98
const router = Router();
109

@@ -16,6 +15,5 @@ router.use('/auth', authRoutes);
1615

1716
// Admin routes
1817
router.use('/admin', adminRoutes);
19-
router.use('/admin/metrics', adminMetricsRoutes);
2018

2119
export default router;

backend/tests/sse.controller.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,23 @@ describe('SSE Controller', () => {
7171
expect(sseService.addClient).toHaveBeenCalled();
7272
});
7373

74+
it('should include user subscriptions when users query params are provided', async () => {
75+
(sseService.isShuttingDown as any).mockReturnValue(false);
76+
(sseService.checkCapacity as any).mockReturnValue({ allowed: true });
77+
(req as any).user = { publicKey: 'GUSER1' };
78+
(prisma.stream.findMany as any).mockResolvedValue([{ streamId: 'stream-1' }]);
79+
req.query = { users: ['GUSER2', 'GUSER3'] };
80+
81+
await subscribe(req as Request, res as Response);
82+
83+
expect(sseService.addClient).toHaveBeenCalledWith(
84+
expect.any(String),
85+
expect.any(Object),
86+
expect.arrayContaining(['stream-1', 'user:GUSER2', 'user:GUSER3', 'user:GUSER1']),
87+
expect.any(String),
88+
);
89+
});
90+
7491
it('should handle zod validation error for query params', async () => {
7592
(sseService.isShuttingDown as any).mockReturnValue(false);
7693
(sseService.checkCapacity as any).mockReturnValue({ allowed: true });

0 commit comments

Comments
 (0)