import { Request as ExpressRequest, Response as ExpressResponse } from "express"; import { Option, tryDo } from "@shared/common/async_utils"; import { rpcClient } from "src/utils/rpc"; import { eventLogger } from "src/utils/logging"; import { QueryOptions } from "winston"; export interface LogEvent { _id: string; label: string; level: string; message: string; timestamp: string; } export interface PollOptions { limit?: string; } function zip(obj: Record): unknown[] { return Object.values(obj).reduce((prev, cur) => [...prev, ...cur], []); } function isNumber(val: unknown): val is number { return !Number.isNaN(Number.parseInt(val as string, 10)); } function queryEvents(opts?: QueryOptions | { includeIds?: boolean }): Promise> { return new Promise((resolve, reject) => { eventLogger.query(opts as QueryOptions, (error, results) => { if (error) { reject(error); } else { resolve(results); } }); }); } export type PollResult = Option<{ events: LogEvent[] }, { error: string }>; type GetResult = Promise>; export const get = async (req: ExpressRequest, res: ExpressResponse): GetResult => { const params = req.query as PollOptions; if (!req.session?.userId) { return res.json({ ok: false, error: "Not logged in, please log in", }); } const { authorised } = await rpcClient.userAuthorised({ userId: req.session.userId }); if (!authorised) { return res.json({ ok: false, error: "Not authorised, please log in", }); } const DEFAULT_LIMIT = 50; const result = await tryDo(queryEvents({ fields: ["level", "message", "label", "timestamp"], limit: isNumber(params.limit) ? +params.limit : DEFAULT_LIMIT, includeIds: true, })); if (!result.ok) { return res.json({ ok: false, error: `Failed to query events. Error: ${result.error}`, }); } return res.json({ ok: true, events: zip(result.result) }); };