fix(api): respect RATE_LIMIT_PER_MIN for tool routes (#272)

Tool endpoints (/api/v1/tools/*) now honor the RATE_LIMIT_PER_MIN env var instead of a hardcoded 60/min: `0` disables per-tool limiting, `>0` uses the configured value, and unset falls back to 60. Merged on top of the section-based route refactor (#280).

Fixes #271.
This commit is contained in:
Luciano Godoy
2026-06-21 23:39:55 +08:00
committed by GitHub
parent 5d5117acf7
commit ce02ce1348
2 changed files with 59 additions and 185 deletions
+10 -1
View File
@@ -223,11 +223,20 @@ export function createToolRoute<T>(app: FastifyInstance, config: ToolRouteConfig
}; };
toolRegistry.set(config.toolId, resolved); toolRegistry.set(config.toolId, resolved);
// Set up rate limiting
const toolRateLimit =
env.RATE_LIMIT_PER_MIN === 0
? false
: {
max: env.RATE_LIMIT_PER_MIN || 60, // Keep fallback in case env var is not set
timeWindow: "1 minute",
};
app.post( app.post(
config.section config.section
? `/api/v1/tools/${config.section}/${config.toolId}` ? `/api/v1/tools/${config.section}/${config.toolId}`
: apiToolPath(config.toolId), : apiToolPath(config.toolId),
{ config: { rateLimit: { max: 60, timeWindow: "1 minute" } } }, { config: { rateLimit: toolRateLimit } },
async (request: FastifyRequest, reply: FastifyReply) => { async (request: FastifyRequest, reply: FastifyReply) => {
// Check per-tool access before processing uploads // Check per-tool access before processing uploads
const authUser = getAuthUser(request); const authUser = getAuthUser(request);
+49 -184
View File
@@ -858,9 +858,7 @@
"id": "ocr-pdf", "id": "ocr-pdf",
"name": "PDF OCR", "name": "PDF OCR",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "long", "executionHint": "long",
"isAI": true "isAI": true
}, },
@@ -2622,10 +2620,7 @@
"id": "gif-webp", "id": "gif-webp",
"name": "GIF/WebP Converter", "name": "GIF/WebP Converter",
"modality": "image", "modality": "image",
"acceptedInputs": [ "acceptedInputs": [".gif", ".webp"],
".gif",
".webp"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -2768,10 +2763,7 @@
"id": "svg-to-raster", "id": "svg-to-raster",
"name": "SVG to Raster", "name": "SVG to Raster",
"modality": "image", "modality": "image",
"acceptedInputs": [ "acceptedInputs": [".svg", ".svgz"],
".svg",
".svgz"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -2869,9 +2861,7 @@
"id": "pdf-to-image", "id": "pdf-to-image",
"name": "PDF to Image", "name": "PDF to Image",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -3287,9 +3277,7 @@
"id": "gif-to-video", "id": "gif-to-video",
"name": "GIF to Video", "name": "GIF to Video",
"modality": "video", "modality": "video",
"acceptedInputs": [ "acceptedInputs": [".gif"],
".gif"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -3895,9 +3883,7 @@
"id": "merge-pdf", "id": "merge-pdf",
"name": "Merge PDFs", "name": "Merge PDFs",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -3905,9 +3891,7 @@
"id": "split-pdf", "id": "split-pdf",
"name": "Split PDF", "name": "Split PDF",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -3915,9 +3899,7 @@
"id": "compress-pdf", "id": "compress-pdf",
"name": "Compress PDF", "name": "Compress PDF",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -3925,9 +3907,7 @@
"id": "rotate-pdf", "id": "rotate-pdf",
"name": "Rotate PDF", "name": "Rotate PDF",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -3935,13 +3915,7 @@
"id": "convert-document", "id": "convert-document",
"name": "Convert Document", "name": "Convert Document",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".docx", ".doc", ".odt", ".rtf", ".txt"],
".docx",
".doc",
".odt",
".rtf",
".txt"
],
"executionHint": "long", "executionHint": "long",
"isAI": false "isAI": false
}, },
@@ -3949,11 +3923,7 @@
"id": "convert-presentation", "id": "convert-presentation",
"name": "Convert Presentation", "name": "Convert Presentation",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pptx", ".ppt", ".odp"],
".pptx",
".ppt",
".odp"
],
"executionHint": "long", "executionHint": "long",
"isAI": false "isAI": false
}, },
@@ -3961,12 +3931,7 @@
"id": "convert-spreadsheet", "id": "convert-spreadsheet",
"name": "Convert Spreadsheet", "name": "Convert Spreadsheet",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".xlsx", ".xls", ".ods", ".csv"],
".xlsx",
".xls",
".ods",
".csv"
],
"executionHint": "long", "executionHint": "long",
"isAI": false "isAI": false
}, },
@@ -3974,12 +3939,7 @@
"id": "excel-to-pdf", "id": "excel-to-pdf",
"name": "Excel to PDF", "name": "Excel to PDF",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".xlsx", ".xls", ".ods", ".csv"],
".xlsx",
".xls",
".ods",
".csv"
],
"executionHint": "long", "executionHint": "long",
"isAI": false "isAI": false
}, },
@@ -3987,13 +3947,7 @@
"id": "word-to-pdf", "id": "word-to-pdf",
"name": "Word to PDF", "name": "Word to PDF",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".docx", ".doc", ".odt", ".rtf", ".txt"],
".docx",
".doc",
".odt",
".rtf",
".txt"
],
"executionHint": "long", "executionHint": "long",
"isAI": false "isAI": false
}, },
@@ -4001,9 +3955,7 @@
"id": "extract-pages", "id": "extract-pages",
"name": "Extract Pages", "name": "Extract Pages",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4011,9 +3963,7 @@
"id": "remove-pages", "id": "remove-pages",
"name": "Remove Pages", "name": "Remove Pages",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4021,9 +3971,7 @@
"id": "organize-pdf", "id": "organize-pdf",
"name": "Organize PDF", "name": "Organize PDF",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4031,9 +3979,7 @@
"id": "protect-pdf", "id": "protect-pdf",
"name": "Protect PDF", "name": "Protect PDF",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4041,9 +3987,7 @@
"id": "unlock-pdf", "id": "unlock-pdf",
"name": "Unlock PDF", "name": "Unlock PDF",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4051,9 +3995,7 @@
"id": "repair-pdf", "id": "repair-pdf",
"name": "Repair PDF", "name": "Repair PDF",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4061,9 +4003,7 @@
"id": "linearize-pdf", "id": "linearize-pdf",
"name": "Web-Optimize PDF", "name": "Web-Optimize PDF",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4071,9 +4011,7 @@
"id": "grayscale-pdf", "id": "grayscale-pdf",
"name": "Grayscale PDF", "name": "Grayscale PDF",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4081,9 +4019,7 @@
"id": "pdfa-convert", "id": "pdfa-convert",
"name": "PDF/A Convert", "name": "PDF/A Convert",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4091,9 +4027,7 @@
"id": "crop-pdf", "id": "crop-pdf",
"name": "Crop PDF", "name": "Crop PDF",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4101,9 +4035,7 @@
"id": "nup-pdf", "id": "nup-pdf",
"name": "N-up PDF", "name": "N-up PDF",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4111,9 +4043,7 @@
"id": "booklet-pdf", "id": "booklet-pdf",
"name": "Booklet PDF", "name": "Booklet PDF",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4121,9 +4051,7 @@
"id": "watermark-pdf", "id": "watermark-pdf",
"name": "Watermark PDF", "name": "Watermark PDF",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4131,9 +4059,7 @@
"id": "pdf-page-numbers", "id": "pdf-page-numbers",
"name": "PDF Page Numbers", "name": "PDF Page Numbers",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4141,9 +4067,7 @@
"id": "flatten-pdf", "id": "flatten-pdf",
"name": "Flatten PDF", "name": "Flatten PDF",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4151,9 +4075,7 @@
"id": "redact-pdf", "id": "redact-pdf",
"name": "Redact PDF", "name": "Redact PDF",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4161,9 +4083,7 @@
"id": "pdf-to-text", "id": "pdf-to-text",
"name": "PDF to Text", "name": "PDF to Text",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4171,9 +4091,7 @@
"id": "pdf-to-word", "id": "pdf-to-word",
"name": "PDF to Word", "name": "PDF to Word",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "long", "executionHint": "long",
"isAI": false "isAI": false
}, },
@@ -4181,9 +4099,7 @@
"id": "pdf-metadata", "id": "pdf-metadata",
"name": "PDF Metadata", "name": "PDF Metadata",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pdf"],
".pdf"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4191,11 +4107,7 @@
"id": "powerpoint-to-pdf", "id": "powerpoint-to-pdf",
"name": "PowerPoint to PDF", "name": "PowerPoint to PDF",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".pptx", ".ppt", ".odp"],
".pptx",
".ppt",
".odp"
],
"executionHint": "long", "executionHint": "long",
"isAI": false "isAI": false
}, },
@@ -4203,10 +4115,7 @@
"id": "html-to-pdf", "id": "html-to-pdf",
"name": "HTML to PDF", "name": "HTML to PDF",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".html", ".htm"],
".html",
".htm"
],
"executionHint": "long", "executionHint": "long",
"isAI": false "isAI": false
}, },
@@ -4214,10 +4123,7 @@
"id": "markdown-to-docx", "id": "markdown-to-docx",
"name": "Markdown to Word", "name": "Markdown to Word",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".md", ".markdown"],
".md",
".markdown"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4225,10 +4131,7 @@
"id": "markdown-to-html", "id": "markdown-to-html",
"name": "Markdown to HTML", "name": "Markdown to HTML",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".md", ".markdown"],
".md",
".markdown"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4236,10 +4139,7 @@
"id": "markdown-to-pdf", "id": "markdown-to-pdf",
"name": "Markdown to PDF", "name": "Markdown to PDF",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".md", ".markdown"],
".md",
".markdown"
],
"executionHint": "long", "executionHint": "long",
"isAI": false "isAI": false
}, },
@@ -4247,9 +4147,7 @@
"id": "epub-convert", "id": "epub-convert",
"name": "Convert EPUB", "name": "Convert EPUB",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".epub"],
".epub"
],
"executionHint": "long", "executionHint": "long",
"isAI": false "isAI": false
}, },
@@ -4257,12 +4155,7 @@
"id": "to-epub", "id": "to-epub",
"name": "Convert to EPUB", "name": "Convert to EPUB",
"modality": "document", "modality": "document",
"acceptedInputs": [ "acceptedInputs": [".docx", ".md", ".html", ".txt"],
".docx",
".md",
".html",
".txt"
],
"executionHint": "long", "executionHint": "long",
"isAI": false "isAI": false
}, },
@@ -4270,10 +4163,7 @@
"id": "chart-maker", "id": "chart-maker",
"name": "Chart Maker", "name": "Chart Maker",
"modality": "file", "modality": "file",
"acceptedInputs": [ "acceptedInputs": [".csv", ".json"],
".csv",
".json"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4281,11 +4171,7 @@
"id": "csv-excel", "id": "csv-excel",
"name": "CSV to Excel", "name": "CSV to Excel",
"modality": "file", "modality": "file",
"acceptedInputs": [ "acceptedInputs": [".csv", ".tsv", ".xlsx"],
".csv",
".tsv",
".xlsx"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4293,11 +4179,7 @@
"id": "csv-json", "id": "csv-json",
"name": "CSV to JSON", "name": "CSV to JSON",
"modality": "file", "modality": "file",
"acceptedInputs": [ "acceptedInputs": [".csv", ".tsv", ".json"],
".csv",
".tsv",
".json"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4305,10 +4187,7 @@
"id": "json-xml", "id": "json-xml",
"name": "JSON to XML", "name": "JSON to XML",
"modality": "file", "modality": "file",
"acceptedInputs": [ "acceptedInputs": [".json", ".xml"],
".json",
".xml"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4316,10 +4195,7 @@
"id": "split-csv", "id": "split-csv",
"name": "Split CSV", "name": "Split CSV",
"modality": "file", "modality": "file",
"acceptedInputs": [ "acceptedInputs": [".csv", ".tsv"],
".csv",
".tsv"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4327,10 +4203,7 @@
"id": "merge-csvs", "id": "merge-csvs",
"name": "Merge CSVs", "name": "Merge CSVs",
"modality": "file", "modality": "file",
"acceptedInputs": [ "acceptedInputs": [".csv", ".tsv"],
".csv",
".tsv"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4338,11 +4211,7 @@
"id": "yaml-json", "id": "yaml-json",
"name": "YAML / JSON", "name": "YAML / JSON",
"modality": "file", "modality": "file",
"acceptedInputs": [ "acceptedInputs": [".yaml", ".yml", ".json"],
".yaml",
".yml",
".json"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4350,9 +4219,7 @@
"id": "xml-to-csv", "id": "xml-to-csv",
"name": "XML to CSV", "name": "XML to CSV",
"modality": "file", "modality": "file",
"acceptedInputs": [ "acceptedInputs": [".xml"],
".xml"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
}, },
@@ -4368,9 +4235,7 @@
"id": "extract-zip", "id": "extract-zip",
"name": "Extract ZIP", "name": "Extract ZIP",
"modality": "file", "modality": "file",
"acceptedInputs": [ "acceptedInputs": [".zip"],
".zip"
],
"executionHint": "fast", "executionHint": "fast",
"isAI": false "isAI": false
} }