mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage-server
synced 2026-05-18 20:08:19 +00:00
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
- LogStreamService: ring buffer (500 entries) + getRecentLogs() method - SupervisorController: GET /api/supervisor/logs/recent returns buffered log entries so the desktop log panel shows history on tab open, not just live stream Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
96 lines
3.4 KiB
TypeScript
96 lines
3.4 KiB
TypeScript
import { Controller, Get, Post, Body, Query, Sse, Logger } from '@nestjs/common';
|
|
import { Observable, filter, map } from 'rxjs';
|
|
import { SupervisorService } from './supervisor.service';
|
|
import { LogStreamService } from '../logging/log-stream.service';
|
|
|
|
@Controller('api/supervisor')
|
|
export class SupervisorController {
|
|
private readonly logger = new Logger(SupervisorController.name);
|
|
|
|
constructor(private readonly supervisor: SupervisorService) {}
|
|
|
|
@Get('active-calls')
|
|
getActiveCalls() {
|
|
return this.supervisor.getActiveCalls();
|
|
}
|
|
|
|
@Sse('active-calls/stream')
|
|
streamActiveCalls(): Observable<MessageEvent> {
|
|
this.logger.log('[SSE] Active calls stream opened');
|
|
return this.supervisor.activeCallSubject.pipe(
|
|
map(event => ({
|
|
data: JSON.stringify(event),
|
|
} as MessageEvent)),
|
|
);
|
|
}
|
|
|
|
@Get('team-performance')
|
|
async getTeamPerformance(@Query('date') date?: string) {
|
|
const targetDate = date ?? new Date().toISOString().split('T')[0];
|
|
this.logger.log(`Team performance: date=${targetDate}`);
|
|
return this.supervisor.getTeamPerformance(targetDate);
|
|
}
|
|
|
|
@Post('call-event')
|
|
handleCallEvent(@Body() body: any) {
|
|
const event = body.data ?? body;
|
|
this.logger.log(`[CALL-EVENT] ${JSON.stringify(event)}`);
|
|
this.supervisor.handleCallEvent(event);
|
|
return { received: true };
|
|
}
|
|
|
|
@Post('agent-event')
|
|
handleAgentEvent(@Body() body: any) {
|
|
const event = body.data ?? body;
|
|
this.logger.log(`[AGENT-EVENT] ${JSON.stringify(event)}`);
|
|
this.supervisor.handleAgentEvent(event);
|
|
return { received: true };
|
|
}
|
|
|
|
@Get('agent-state')
|
|
getAgentState(@Query('agentId') agentId: string) {
|
|
const state = this.supervisor.getAgentState(agentId);
|
|
return state ?? { state: 'offline', timestamp: null };
|
|
}
|
|
|
|
@Sse('agent-state/stream')
|
|
streamAgentState(@Query('agentId') agentId: string): Observable<MessageEvent> {
|
|
this.logger.log(`[SSE] Agent state stream opened for ${agentId}`);
|
|
return this.supervisor.agentStateSubject.pipe(
|
|
filter(event => event.agentId === agentId),
|
|
map(event => ({
|
|
data: JSON.stringify({ state: event.state, timestamp: event.timestamp }),
|
|
} as MessageEvent)),
|
|
);
|
|
}
|
|
|
|
// Worklist SSE — broadcast to all connected agents. When a missed
|
|
// call is created by the webhook, this fires immediately so agents
|
|
// don't wait for the 30s worklist poll. The payload includes the
|
|
// caller's phone + name for a toast notification.
|
|
@Sse('worklist/stream')
|
|
streamWorklistUpdates(): Observable<MessageEvent> {
|
|
this.logger.log('[SSE] Worklist stream opened');
|
|
return this.supervisor.worklistSubject.pipe(
|
|
map(event => ({
|
|
data: JSON.stringify(event),
|
|
} as MessageEvent)),
|
|
);
|
|
}
|
|
|
|
@Get('logs/recent')
|
|
getRecentLogs(@Query('limit') limit?: string) {
|
|
return LogStreamService.instance.getRecentLogs(limit ? parseInt(limit, 10) : 200);
|
|
}
|
|
|
|
@Sse('logs/stream')
|
|
streamLogs(): Observable<MessageEvent> {
|
|
this.logger.log('[SSE] Log stream opened');
|
|
return LogStreamService.instance.logSubject.pipe(
|
|
map(entry => ({
|
|
data: JSON.stringify(entry),
|
|
} as MessageEvent)),
|
|
);
|
|
}
|
|
}
|