diff --git a/apps/api/src/controllers/v1/types.ts b/apps/api/src/controllers/v1/types.ts index db8be9af..91f436ae 100644 --- a/apps/api/src/controllers/v1/types.ts +++ b/apps/api/src/controllers/v1/types.ts @@ -1,4 +1,4 @@ -import { Request } from "express"; +import { Request, Response } from "express"; import { z } from "zod"; import { isUrlBlocked } from "../../scraper/WebScraper/utils/blocklist"; import { PageOptions } from "../../lib/entities"; @@ -249,6 +249,12 @@ export interface RequestWithAuth< account?: Account; } +export interface ResponseWithSentry< + ResBody = undefined, +> extends Response { + sentry?: string, +} + export function legacyCrawlerOptions(x: CrawlerOptions) { return { includes: x.includePaths, diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index 9bbc6036..bc403ddc 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -1,7 +1,7 @@ import "dotenv/config"; import "./services/sentry" import * as Sentry from "@sentry/node"; -import express from "express"; +import express, { NextFunction, Request, Response } from "express"; import bodyParser from "body-parser"; import cors from "cors"; import { getScrapeQueue } from "./services/queue-service"; @@ -18,8 +18,9 @@ import CacheableLookup from 'cacheable-lookup'; import { v1Router } from "./routes/v1"; import expressWs from "express-ws"; import { crawlStatusWSController } from "./controllers/v1/crawl-status-ws"; - - +import { ErrorResponse, ResponseWithSentry } from "./controllers/v1/types"; +import { ZodError } from "zod"; +import { v4 as uuidv4 } from "uuid"; const { createBullBoard } = require("@bull-board/api"); const { BullAdapter } = require("@bull-board/api/bullAdapter"); @@ -191,6 +192,27 @@ if (cluster.isMaster) { Sentry.setupExpressErrorHandler(app); + app.use((err: unknown, req: Request<{}, ErrorResponse, undefined>, res: ResponseWithSentry, next: NextFunction) => { + if (err instanceof ZodError) { + res.status(400).json({ success: false, error: "Bad Request", details: err.errors }); + } else { + const id = res.sentry ?? uuidv4(); + let verbose = JSON.stringify(err); + if (verbose === "{}") { + if (err instanceof Error) { + verbose = JSON.stringify({ + message: err.message, + name: err.name, + stack: err.stack, + }); + } + } + + Logger.error("Error occurred in request! (" + req.path + ") -- ID " + id + " -- " + verbose); + res.status(500).json({ success: false, error: "An unexpected error occurred. Please contact hello@firecrawl.com for help. Your exception ID is " + id }); + } + }); + Logger.info(`Worker ${process.pid} started`); } diff --git a/apps/api/src/routes/v1.ts b/apps/api/src/routes/v1.ts index 51f07c85..500a172e 100644 --- a/apps/api/src/routes/v1.ts +++ b/apps/api/src/routes/v1.ts @@ -142,24 +142,3 @@ v1Router.ws( // Health/Probe routes // v1Router.get("/health/liveness", livenessController); // v1Router.get("/health/readiness", readinessController); - -v1Router.use((err: unknown, req: Request<{}, ErrorResponse, undefined>, res: Response, next: NextFunction) => { - if (err instanceof ZodError) { - res.status(400).json({ success: false, error: "Bad Request", details: err.errors }); - } else { - const id = uuidv4(); - let verbose = JSON.stringify(err); - if (verbose === "{}") { - if (err instanceof Error) { - verbose = JSON.stringify({ - message: err.message, - name: err.name, - stack: err.stack, - }); - } - } - - Logger.error("Error occurred in request! (" + req.path + ") -- ID " + id + " -- " + verbose); - res.status(500).json({ success: false, error: "An unexpected error occurred. Please contact hello@firecrawl.com for help. Your exception ID is " + id + "" }); - } -});