From 124beae2bcf4f99a10f73cce932f7cbf80106273 Mon Sep 17 00:00:00 2001 From: indifferentketchup Date: Fri, 15 May 2026 15:10:20 +0000 Subject: [PATCH] batch3 T3 review fix: swap req.user! for requireUser; document ws/user guard Replaces six non-null assertions on req.user with the requireUser helper from auth.ts, which throws a descriptive error if the auth hook didn't populate req.user. Adds an inline comment in /api/ws/user explaining the manual auth check is defensive (the global hook already enforces auth). Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/server/src/routes/messages.ts | 5 +++-- apps/server/src/routes/projects.ts | 5 +++-- apps/server/src/routes/sessions.ts | 5 +++-- apps/server/src/routes/ws.ts | 2 ++ 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/server/src/routes/messages.ts b/apps/server/src/routes/messages.ts index 59f97f4..4745afd 100644 --- a/apps/server/src/routes/messages.ts +++ b/apps/server/src/routes/messages.ts @@ -2,6 +2,7 @@ import type { FastifyInstance } from 'fastify'; import { z } from 'zod'; import type { Sql } from '../db.js'; import type { Message, Session } from '../types/api.js'; +import { requireUser } from '../auth.js'; const SendBody = z.object({ content: z.string().min(1).max(64_000), @@ -76,7 +77,7 @@ export function registerMessageRoutes( result.user_message_id, parsed.data.content ); - handlers.enqueueInference(req.params.id, result.assistant_message_id, req.user!); + handlers.enqueueInference(req.params.id, result.assistant_message_id, requireUser(req)); reply.code(202); return result; @@ -132,7 +133,7 @@ export function registerMessageRoutes( }); handlers.publishMessagesDeleted(sessionId, deletedIds); - handlers.enqueueInference(sessionId, newAssistantId, req.user!); + handlers.enqueueInference(sessionId, newAssistantId, requireUser(req)); reply.code(202); return { assistant_message_id: newAssistantId }; diff --git a/apps/server/src/routes/projects.ts b/apps/server/src/routes/projects.ts index 6b34e75..50e72f4 100644 --- a/apps/server/src/routes/projects.ts +++ b/apps/server/src/routes/projects.ts @@ -6,6 +6,7 @@ import type { Sql } from '../db.js'; import type { Config } from '../config.js'; import type { Broker } from '../services/broker.js'; import type { Project, AvailableProject } from '../types/api.js'; +import { requireUser } from '../auth.js'; const AddProjectBody = z.object({ path: z.string().min(1), @@ -73,7 +74,7 @@ export function registerProjectRoutes( VALUES (${name}, ${resolved.real}) RETURNING id, name, path, added_at, last_session_id `; - broker.publishUser(req.user!, { type: 'project_created', project: row as unknown as Project }); + broker.publishUser(requireUser(req), { type: 'project_created', project: row as unknown as Project }); reply.code(201); return row; } catch (err) { @@ -92,7 +93,7 @@ export function registerProjectRoutes( reply.code(404); return { error: 'not found' }; } - broker.publishUser(req.user!, { type: 'project_deleted', project_id: id }); + broker.publishUser(requireUser(req), { type: 'project_deleted', project_id: id }); reply.code(204); return null; }); diff --git a/apps/server/src/routes/sessions.ts b/apps/server/src/routes/sessions.ts index 4c34fb0..9c2a5d0 100644 --- a/apps/server/src/routes/sessions.ts +++ b/apps/server/src/routes/sessions.ts @@ -5,6 +5,7 @@ import type { Config } from '../config.js'; import type { Broker } from '../services/broker.js'; import type { Session } from '../types/api.js'; import { getSetting } from './settings.js'; +import { requireUser } from '../auth.js'; const CreateBody = z.object({ name: z.string().min(1).max(200).optional(), @@ -88,7 +89,7 @@ export function registerSessionRoutes( `; return session!; }); - broker.publishUser(req.user!, { + broker.publishUser(requireUser(req), { type: 'session_created', session: row, project_id: row.project_id, @@ -149,7 +150,7 @@ export function registerSessionRoutes( return { error: 'not found' }; } const project_id = deleted[0]!.project_id; - broker.publishUser(req.user!, { type: 'session_deleted', session_id: id, project_id }); + broker.publishUser(requireUser(req), { type: 'session_deleted', session_id: id, project_id }); reply.code(204); return null; } diff --git a/apps/server/src/routes/ws.ts b/apps/server/src/routes/ws.ts index 994e5ec..7877c55 100644 --- a/apps/server/src/routes/ws.ts +++ b/apps/server/src/routes/ws.ts @@ -46,6 +46,8 @@ export function registerWebSocket( app.get('/api/ws/user', { websocket: true }, async (socket, req) => { const user = req.user; + // defensive: global auth hook (auth.ts) already rejects unauthenticated /api/* requests; + // keep the explicit check here to close the WS cleanly (1008) rather than throwing. if (!user) { socket.close(1008, 'unauthenticated'); return;