|
@@ -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) });
|
|
|
|
+};
|