feat: add sessions stats endpoint

This commit is contained in:
Carlos Valente
2024-10-20 22:19:33 +02:00
committed by Carlos Valente
parent 71c468d069
commit 82810f1cb1
8 changed files with 71 additions and 3 deletions
@@ -33,6 +33,7 @@ export class SocketServer implements IAdapter {
private wss: WebSocketServer | null; private wss: WebSocketServer | null;
private readonly clients: Map<string, Client>; private readonly clients: Map<string, Client>;
private lastConnection: Date | null = null;
constructor() { constructor() {
if (instance) { if (instance) {
@@ -58,6 +59,7 @@ export class SocketServer implements IAdapter {
path: '', path: '',
}); });
this.lastConnection = new Date();
logger.info(LogOrigin.Client, `${this.clients.size} Connections with new: ${clientId}`); logger.info(LogOrigin.Client, `${this.clients.size} Connections with new: ${clientId}`);
ws.send( ws.send(
@@ -171,6 +173,13 @@ export class SocketServer implements IAdapter {
}); });
} }
getStats() {
return {
connectedClients: this.clients.size,
lastConnection: this.lastConnection,
};
}
private sendClientList(): void { private sendClientList(): void {
const payload = Object.fromEntries(this.clients.entries()); const payload = Object.fromEntries(this.clients.entries());
this.sendAsJson({ type: 'client-list', payload }); this.sendAsJson({ type: 'client-list', payload });
@@ -1,10 +1,20 @@
import { getErrorMessage } from 'ontime-utils'; import { getErrorMessage } from 'ontime-utils';
import { ErrorResponse, GetInfo } from 'ontime-types'; import { ErrorResponse, GetInfo, SessionStats } from 'ontime-types';
import type { Request, Response } from 'express'; import type { Request, Response } from 'express';
import * as sessionService from './session.service.js'; import * as sessionService from './session.service.js';
export async function getSessionStats(_req: Request, res: Response<SessionStats | ErrorResponse>) {
try {
const stats = await sessionService.getSessionStats();
res.status(200).send(stats);
} catch (error) {
const message = getErrorMessage(error);
res.status(500).send({ message });
}
}
export async function getInfo(_req: Request, res: Response<GetInfo | ErrorResponse>) { export async function getInfo(_req: Request, res: Response<GetInfo | ErrorResponse>) {
try { try {
const info = await sessionService.getInfo(); const info = await sessionService.getInfo();
@@ -1,7 +1,8 @@
import express from 'express'; import express from 'express';
import { getInfo } from './session.controller.js'; import { getInfo, getSessionStats } from './session.controller.js';
export const router = express.Router(); export const router = express.Router();
router.get('/', getSessionStats);
router.get('/info', getInfo); router.get('/info', getInfo);
@@ -1,8 +1,32 @@
import { GetInfo } from 'ontime-types'; import { GetInfo, SessionStats } from 'ontime-types';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js'; import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { publicFiles } from '../../setup/index.js'; import { publicFiles } from '../../setup/index.js';
import { getNetworkInterfaces } from '../../utils/networkInterfaces.js'; import { getNetworkInterfaces } from '../../utils/networkInterfaces.js';
import { socket } from '../../adapters/WebsocketAdapter.js';
import { getLastRequest } from '../../api-integration/integration.controller.js';
import { getLastLoadedProject } from '../../services/app-state-service/AppStateService.js';
import { runtimeService } from '../../services/runtime-service/RuntimeService.js';
const startedAt = new Date();
/** Gathers information related to runtime */
export async function getSessionStats(): Promise<SessionStats> {
const { connectedClients, lastConnection } = socket.getStats();
const lastRequest = getLastRequest();
const projectName = await getLastLoadedProject();
const { playback } = runtimeService.getRuntimeState();
return {
startedAt: startedAt.toISOString(),
connectedClients,
lastConnection: lastConnection !== null ? lastConnection.toISOString() : null,
lastRequest: lastRequest !== null ? lastRequest.toISOString() : null,
projectName,
playback,
timezone: startedAt.getTimezoneOffset(),
};
}
/** /**
* Adds business logic to gathering data for the info endpoint * Adds business logic to gathering data for the info endpoint
@@ -19,10 +19,13 @@ import { willCauseRegeneration } from '../services/rundown-service/rundownCacheU
import { handleLegacyMessageConversion } from './integration.legacy.js'; import { handleLegacyMessageConversion } from './integration.legacy.js';
const throttledUpdateEvent = throttle(updateEvent, 20); const throttledUpdateEvent = throttle(updateEvent, 20);
let lastRequest: Date | null = null;
export function dispatchFromAdapter(type: string, payload: unknown, _source?: 'osc' | 'ws' | 'http') { export function dispatchFromAdapter(type: string, payload: unknown, _source?: 'osc' | 'ws' | 'http') {
const action = type.toLowerCase(); const action = type.toLowerCase();
const handler = actionHandlers[action]; const handler = actionHandlers[action];
lastRequest = new Date();
if (handler) { if (handler) {
return handler(payload); return handler(payload);
} else { } else {
@@ -30,6 +33,10 @@ export function dispatchFromAdapter(type: string, payload: unknown, _source?: 'o
} }
} }
export function getLastRequest() {
return lastRequest;
}
type ActionHandler = (payload: unknown) => { payload: unknown }; type ActionHandler = (payload: unknown) => { payload: unknown };
const actionHandlers: Record<string, ActionHandler> = { const actionHandlers: Record<string, ActionHandler> = {
@@ -293,6 +293,10 @@ class RuntimeService {
return success; return success;
} }
public getRuntimeState() {
return { playback: runtimeState.getState().timer.playback };
}
/** /**
* starts event matching given ID * starts event matching given ID
* @param {string} eventId * @param {string} eventId
@@ -1,11 +1,23 @@
import type { OSCSettings } from '../../definitions/core/OscSettings.type.js'; import type { OSCSettings } from '../../definitions/core/OscSettings.type.js';
import type { OntimeRundown } from '../../definitions/core/Rundown.type.js'; import type { OntimeRundown } from '../../definitions/core/Rundown.type.js';
import type { Playback } from '../../definitions/runtime/Playback.type.js';
import type { MaybeString } from '../../utils/utils.type.js';
export type NetworkInterface = { export type NetworkInterface = {
name: string; name: string;
address: string; address: string;
}; };
export interface SessionStats {
startedAt: string;
connectedClients: number;
lastConnection: MaybeString;
lastRequest: MaybeString;
projectName: string;
playback: Playback;
timezone: number;
}
export interface GetInfo { export interface GetInfo {
networkInterfaces: NetworkInterface[]; networkInterfaces: NetworkInterface[];
version: string; version: string;
+1
View File
@@ -51,6 +51,7 @@ export type {
ProjectFileListResponse, ProjectFileListResponse,
MessageResponse, MessageResponse,
RundownPaginated, RundownPaginated,
SessionStats,
} from './api/ontime-controller/BackendResponse.type.js'; } from './api/ontime-controller/BackendResponse.type.js';
export type { RundownCached, NormalisedRundown } from './api/rundown-controller/BackendResponse.type.js'; export type { RundownCached, NormalisedRundown } from './api/rundown-controller/BackendResponse.type.js';