Files
firecrawl/apps/api/src/routes/v1.ts
T

165 lines
5.7 KiB
TypeScript
Raw Normal View History

import express, { NextFunction, Request, Response } from "express";
2024-08-27 09:42:55 -03:00
import { crawlController } from "../controllers/v1/crawl";
2024-08-06 15:24:45 -03:00
// import { crawlStatusController } from "../../src/controllers/v1/crawl-status";
import { scrapeController } from "../../src/controllers/v1/scrape";
2024-08-27 09:42:55 -03:00
import { crawlStatusController } from "../controllers/v1/crawl-status";
import { mapController } from "../controllers/v1/map";
import { ErrorResponse, RequestWithAuth, RequestWithMaybeAuth } from "../controllers/v1/types";
import { RateLimiterMode } from "../types";
2024-08-26 18:48:00 -03:00
import { authenticateUser } from "../controllers/auth";
import { createIdempotencyKey } from "../services/idempotency/create";
import { validateIdempotencyKey } from "../services/idempotency/validate";
import { checkTeamCredits } from "../services/billing/credit_billing";
2024-08-17 01:04:14 +02:00
import expressWs from "express-ws";
import { crawlStatusWSController } from "../controllers/v1/crawl-status-ws";
2024-08-19 13:28:54 -03:00
import { isUrlBlocked } from "../scraper/WebScraper/utils/blocklist";
2024-08-27 09:42:55 -03:00
import { crawlCancelController } from "../controllers/v1/crawl-cancel";
2024-08-27 11:58:42 -03:00
import { Logger } from "../lib/logger";
2024-08-31 14:23:55 -03:00
import { scrapeStatusController } from "../controllers/v1/scrape-status";
2024-08-06 15:24:45 -03:00
// import { crawlPreviewController } from "../../src/controllers/v1/crawlPreview";
// import { crawlJobStatusPreviewController } from "../../src/controllers/v1/status";
// import { searchController } from "../../src/controllers/v1/search";
// import { crawlCancelController } from "../../src/controllers/v1/crawl-cancel";
// import { keyAuthController } from "../../src/controllers/v1/keyAuth";
// import { livenessController } from "../controllers/v1/liveness";
// import { readinessController } from "../controllers/v1/readiness";
2024-08-20 14:39:52 -03:00
function checkCreditsMiddleware(minimum?: number): (req: RequestWithAuth, res: Response, next: NextFunction) => void {
return (req, res, next) => {
(async () => {
2024-08-20 14:39:52 -03:00
if (!minimum && req.body) {
minimum = (req.body as any)?.limit ?? 1;
}
const { success, message, remainingCredits } = await checkTeamCredits(req.auth.team_id, minimum);
if (!success) {
2024-08-27 11:58:42 -03:00
Logger.error(`Insufficient credits: ${JSON.stringify({ team_id: req.auth.team_id, minimum, remainingCredits })}`);
2024-09-04 11:29:32 -03:00
if (!res.headersSent) {
return res.status(402).json({ success: false, error: "Insufficient credits" });
}
}
2024-08-20 14:39:52 -03:00
req.account = { remainingCredits }
next();
})()
.catch(err => next(err));
};
}
2024-08-17 01:04:14 +02:00
export function authMiddleware(rateLimiterMode: RateLimiterMode): (req: RequestWithMaybeAuth, res: Response, next: NextFunction) => void {
return (req, res, next) => {
(async () => {
const { success, team_id, error, status, plan } = await authenticateUser(
req,
res,
rateLimiterMode,
);
if (!success) {
2024-09-04 11:29:32 -03:00
if (!res.headersSent) {
return res.status(status).json({ success: false, error });
}
}
req.auth = { team_id, plan };
next();
})()
.catch(err => next(err));
}
}
function idempotencyMiddleware(req: Request, res: Response, next: NextFunction) {
(async () => {
if (req.headers["x-idempotency-key"]) {
const isIdempotencyValid = await validateIdempotencyKey(req);
if (!isIdempotencyValid) {
2024-09-04 11:29:32 -03:00
if (!res.headersSent) {
return res.status(409).json({ success: false, error: "Idempotency key already used" });
}
}
createIdempotencyKey(req);
}
next();
})()
.catch(err => next(err));
}
2024-08-19 13:28:54 -03:00
function blocklistMiddleware(req: Request, res: Response, next: NextFunction) {
if (typeof req.body.url === "string" && isUrlBlocked(req.body.url)) {
2024-09-04 11:29:32 -03:00
if (!res.headersSent) {
return res.status(403).json({ success: false, error: "URL is blocked. Firecrawl currently does not support social media scraping due to policy restrictions." });
}
2024-08-19 13:28:54 -03:00
}
next();
}
function wrap(controller: (req: Request, res: Response) => Promise<any>): (req: Request, res: Response, next: NextFunction) => any {
return (req, res, next) => {
controller(req, res)
.catch(err => next(err))
}
}
2024-08-17 01:04:14 +02:00
expressWs(express());
2024-08-06 15:24:45 -03:00
export const v1Router = express.Router();
v1Router.post(
2024-08-16 23:48:50 +02:00
"/scrape",
authMiddleware(RateLimiterMode.Scrape),
checkCreditsMiddleware(1),
2024-09-04 11:29:32 -03:00
blocklistMiddleware,
wrap(scrapeController)
);
v1Router.post(
2024-08-16 23:48:50 +02:00
"/crawl",
authMiddleware(RateLimiterMode.Crawl),
2024-08-20 14:39:52 -03:00
checkCreditsMiddleware(),
2024-09-04 11:29:32 -03:00
blocklistMiddleware,
idempotencyMiddleware,
wrap(crawlController)
);
v1Router.post(
2024-08-16 23:48:50 +02:00
"/map",
2024-08-20 12:04:08 -03:00
authMiddleware(RateLimiterMode.Map),
checkCreditsMiddleware(1),
2024-09-04 11:29:32 -03:00
blocklistMiddleware,
wrap(mapController)
);
v1Router.get(
2024-08-16 23:48:50 +02:00
"/crawl/:jobId",
authMiddleware(RateLimiterMode.CrawlStatus),
wrap(crawlStatusController)
);
2024-08-31 14:23:55 -03:00
v1Router.get(
"/scrape/:jobId",
wrap(scrapeStatusController)
);
2024-08-17 01:04:14 +02:00
v1Router.ws(
"/crawl/:jobId",
crawlStatusWSController
);
2024-08-16 23:48:50 +02:00
// v1Router.post("/crawlWebsitePreview", crawlPreviewController);
2024-08-27 09:42:55 -03:00
v1Router.delete(
"/crawl/:jobId",
authMiddleware(RateLimiterMode.Crawl),
crawlCancelController
);
2024-08-16 23:48:50 +02:00
// v1Router.get("/checkJobStatus/:jobId", crawlJobStatusPreviewController);
2024-08-06 15:24:45 -03:00
// // Auth route for key based authentication
2024-08-16 23:48:50 +02:00
// v1Router.get("/keyAuth", keyAuthController);
2024-08-06 15:24:45 -03:00
// // Search routes
2024-08-16 23:48:50 +02:00
// v0Router.post("/search", searchController);
2024-08-06 15:24:45 -03:00
// Health/Probe routes
2024-08-16 23:48:50 +02:00
// v1Router.get("/health/liveness", livenessController);
// v1Router.get("/health/readiness", readinessController);