mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-21 06:59:09 +00:00
feat: add sessions stats endpoint
This commit is contained in:
committed by
Carlos Valente
parent
71c468d069
commit
82810f1cb1
@@ -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;
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user