feat(FN-1745): scope WebSocket channels to project context for multi-project support

- Scope /api/tasks/:id/logs/stream SSE endpoint to resolved project context
- Make badge WebSocket/pubsub channels project-aware using scopeKey
- Fix terminal WebSocket project/service resolution with auth middleware
- Add cross-instance badge broadcast via BadgePubSub with projectId metadata
- Update AGENTS.md documentation for badge WebSocket project-scoping
- Add tests for project-scoped WebSocket and SSE endpoints
- Add tests for project detection in WebSocket upgrade handling
This commit is contained in:
gsxdsm
2026-04-14 08:13:25 -07:00
parent 38d859e843
commit 271f3f7c16
8 changed files with 395 additions and 115 deletions

View File

@@ -304,10 +304,12 @@ describe("API Error Handling Middleware", () => {
describe("Terminal WebSocket heartbeat", () => {
let app: ReturnType<typeof express>;
let server: http.Server;
let store: TaskStore;
beforeEach(() => {
app = express();
server = http.createServer(app);
store = createMockStore();
vi.useFakeTimers();
vi.spyOn(console, "warn").mockImplementation(() => {});
vi.spyOn(console, "info").mockImplementation(() => {});
@@ -349,7 +351,7 @@ describe("Terminal WebSocket heartbeat", () => {
/** Setup terminal WebSocket and trigger a connection */
function setupAndConnect(ws: any, req: any): void {
const wss = setupTerminalWebSocket(app, server);
const wss = setupTerminalWebSocket(app, server, store);
// The function sets up wss on the server's upgrade event.
// We need to access the WebSocketServer directly to emit a connection.
@@ -368,7 +370,7 @@ describe("Terminal WebSocket heartbeat", () => {
mockTerminalService.getSession.mockReturnValue({
id: "session-1",
shell: "/bin/bash",
cwd: "/project",
cwd: "/fake/root",
lastActivityAt: new Date(),
});
@@ -394,7 +396,7 @@ describe("Terminal WebSocket heartbeat", () => {
mockTerminalService.getSession.mockReturnValue({
id: "session-2",
shell: "/bin/bash",
cwd: "/project",
cwd: "/fake/root",
lastActivityAt: new Date(),
});
@@ -420,7 +422,7 @@ describe("Terminal WebSocket heartbeat", () => {
mockTerminalService.getSession.mockReturnValue({
id: "session-3",
shell: "/bin/bash",
cwd: "/project",
cwd: "/fake/root",
lastActivityAt: new Date(),
});
@@ -457,7 +459,7 @@ describe("Terminal WebSocket heartbeat", () => {
mockTerminalService.getSession.mockReturnValue({
id: "stale-session",
shell: "/bin/bash",
cwd: "/project",
cwd: "/fake/root",
lastActivityAt: tenMinutesAgo,
});
@@ -479,7 +481,7 @@ describe("Terminal WebSocket heartbeat", () => {
mockTerminalService.getSession.mockReturnValue({
id: "fresh-session",
shell: "/bin/bash",
cwd: "/project",
cwd: "/fake/root",
lastActivityAt: new Date(Date.now() - 60_000),
});