|  | @@ -0,0 +1,74 @@
 | 
	
		
			
				|  |  | +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<string, unknown[]>): 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<Record<string, unknown[]>> {
 | 
	
		
			
				|  |  | +    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<ExpressResponse<PollResult>>;
 | 
	
		
			
				|  |  | +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) });
 | 
	
		
			
				|  |  | +};
 |