import Dictionary from "Common/Types/Dictionary"; import BadDataException from "Common/Types/Exception/BadDataException"; import { JSONObject } from "Common/Types/JSON"; import Express, { ExpressRequest, ExpressResponse, ExpressRouter, NextFunction, RequestHandler, } from "Common/Server/Utils/Express"; import Response from "Common/Server/Utils/Response"; import TelemetryQueueService from "../../Services/Queue/TelemetryQueueService"; import ClusterKeyAuthorization from "Common/Server/Middleware/ClusterKeyAuthorization"; const router: ExpressRouter = Express.getRouter(); const processIncomingRequest: RequestHandler = async ( req: ExpressRequest, res: ExpressResponse, next: NextFunction, ): Promise => { try { const requestHeaders: Dictionary = req.headers as Dictionary; const requestBody: string | JSONObject = req.body as string | JSONObject; const monitorSecretKeyAsString: string | undefined = req.params["secretkey"]; if (!monitorSecretKeyAsString) { throw new BadDataException("Invalid Secret Key"); } // Return response immediately Response.sendEmptySuccessResponse(req, res); // Add to queue for asynchronous processing await TelemetryQueueService.addIncomingRequestIngestJob({ secretKey: monitorSecretKeyAsString, requestHeaders: requestHeaders, requestBody: requestBody, requestMethod: req.method, }); return; } catch (err) { return next(err); } }; router.post( "/incoming-request/:secretkey", async ( req: ExpressRequest, res: ExpressResponse, next: NextFunction, ): Promise => { processIncomingRequest(req, res, next); }, ); router.get( "/incoming-request/:secretkey", async ( req: ExpressRequest, res: ExpressResponse, next: NextFunction, ): Promise => { processIncomingRequest(req, res, next); }, ); // Queue stats endpoint router.get( "/incoming-request/queue/stats", ClusterKeyAuthorization.isAuthorizedServiceMiddleware, async ( req: ExpressRequest, res: ExpressResponse, next: NextFunction, ): Promise => { try { const stats: { waiting: number; active: number; completed: number; failed: number; delayed: number; total: number; } = await TelemetryQueueService.getQueueStats(); return Response.sendJsonObjectResponse(req, res, stats); } catch (err) { return next(err); } }, ); // Queue size endpoint router.get( "/incoming-request/queue/size", ClusterKeyAuthorization.isAuthorizedServiceMiddleware, async ( req: ExpressRequest, res: ExpressResponse, next: NextFunction, ): Promise => { try { const size: number = await TelemetryQueueService.getQueueSize(); return Response.sendJsonObjectResponse(req, res, { size }); } catch (err) { return next(err); } }, ); // Queue failed jobs endpoint router.get( "/incoming-request/queue/failed", ClusterKeyAuthorization.isAuthorizedServiceMiddleware, async ( req: ExpressRequest, res: ExpressResponse, next: NextFunction, ): Promise => { try { // Parse pagination parameters from query string const start: number = parseInt(req.query["start"] as string) || 0; const end: number = parseInt(req.query["end"] as string) || 100; const failedJobs: Array<{ id: string; name: string; data: any; failedReason: string; stackTrace?: string; processedOn: Date | null; finishedOn: Date | null; attemptsMade: number; }> = await TelemetryQueueService.getFailedJobs({ start, end, }); return Response.sendJsonObjectResponse(req, res, { failedJobs, pagination: { start, end, count: failedJobs.length, }, }); } catch (err) { return next(err); } }, ); export default router;