diff --git a/apps/api/src/routes/tool-factory.ts b/apps/api/src/routes/tool-factory.ts index 380a9ea2..c30605f9 100644 --- a/apps/api/src/routes/tool-factory.ts +++ b/apps/api/src/routes/tool-factory.ts @@ -223,11 +223,20 @@ export function createToolRoute(app: FastifyInstance, config: ToolRouteConfig }; 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( config.section ? `/api/v1/tools/${config.section}/${config.toolId}` : apiToolPath(config.toolId), - { config: { rateLimit: { max: 60, timeWindow: "1 minute" } } }, + { config: { rateLimit: toolRateLimit } }, async (request: FastifyRequest, reply: FastifyReply) => { // Check per-tool access before processing uploads const authUser = getAuthUser(request); diff --git a/tests/qa/tools-meta.json b/tests/qa/tools-meta.json index 4c930d41..a3445c5a 100644 --- a/tests/qa/tools-meta.json +++ b/tests/qa/tools-meta.json @@ -858,9 +858,7 @@ "id": "ocr-pdf", "name": "PDF OCR", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "long", "isAI": true }, @@ -2622,10 +2620,7 @@ "id": "gif-webp", "name": "GIF/WebP Converter", "modality": "image", - "acceptedInputs": [ - ".gif", - ".webp" - ], + "acceptedInputs": [".gif", ".webp"], "executionHint": "fast", "isAI": false }, @@ -2768,10 +2763,7 @@ "id": "svg-to-raster", "name": "SVG to Raster", "modality": "image", - "acceptedInputs": [ - ".svg", - ".svgz" - ], + "acceptedInputs": [".svg", ".svgz"], "executionHint": "fast", "isAI": false }, @@ -2869,9 +2861,7 @@ "id": "pdf-to-image", "name": "PDF to Image", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -3287,9 +3277,7 @@ "id": "gif-to-video", "name": "GIF to Video", "modality": "video", - "acceptedInputs": [ - ".gif" - ], + "acceptedInputs": [".gif"], "executionHint": "fast", "isAI": false }, @@ -3895,9 +3883,7 @@ "id": "merge-pdf", "name": "Merge PDFs", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -3905,9 +3891,7 @@ "id": "split-pdf", "name": "Split PDF", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -3915,9 +3899,7 @@ "id": "compress-pdf", "name": "Compress PDF", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -3925,9 +3907,7 @@ "id": "rotate-pdf", "name": "Rotate PDF", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -3935,13 +3915,7 @@ "id": "convert-document", "name": "Convert Document", "modality": "document", - "acceptedInputs": [ - ".docx", - ".doc", - ".odt", - ".rtf", - ".txt" - ], + "acceptedInputs": [".docx", ".doc", ".odt", ".rtf", ".txt"], "executionHint": "long", "isAI": false }, @@ -3949,11 +3923,7 @@ "id": "convert-presentation", "name": "Convert Presentation", "modality": "document", - "acceptedInputs": [ - ".pptx", - ".ppt", - ".odp" - ], + "acceptedInputs": [".pptx", ".ppt", ".odp"], "executionHint": "long", "isAI": false }, @@ -3961,12 +3931,7 @@ "id": "convert-spreadsheet", "name": "Convert Spreadsheet", "modality": "document", - "acceptedInputs": [ - ".xlsx", - ".xls", - ".ods", - ".csv" - ], + "acceptedInputs": [".xlsx", ".xls", ".ods", ".csv"], "executionHint": "long", "isAI": false }, @@ -3974,12 +3939,7 @@ "id": "excel-to-pdf", "name": "Excel to PDF", "modality": "document", - "acceptedInputs": [ - ".xlsx", - ".xls", - ".ods", - ".csv" - ], + "acceptedInputs": [".xlsx", ".xls", ".ods", ".csv"], "executionHint": "long", "isAI": false }, @@ -3987,13 +3947,7 @@ "id": "word-to-pdf", "name": "Word to PDF", "modality": "document", - "acceptedInputs": [ - ".docx", - ".doc", - ".odt", - ".rtf", - ".txt" - ], + "acceptedInputs": [".docx", ".doc", ".odt", ".rtf", ".txt"], "executionHint": "long", "isAI": false }, @@ -4001,9 +3955,7 @@ "id": "extract-pages", "name": "Extract Pages", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -4011,9 +3963,7 @@ "id": "remove-pages", "name": "Remove Pages", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -4021,9 +3971,7 @@ "id": "organize-pdf", "name": "Organize PDF", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -4031,9 +3979,7 @@ "id": "protect-pdf", "name": "Protect PDF", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -4041,9 +3987,7 @@ "id": "unlock-pdf", "name": "Unlock PDF", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -4051,9 +3995,7 @@ "id": "repair-pdf", "name": "Repair PDF", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -4061,9 +4003,7 @@ "id": "linearize-pdf", "name": "Web-Optimize PDF", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -4071,9 +4011,7 @@ "id": "grayscale-pdf", "name": "Grayscale PDF", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -4081,9 +4019,7 @@ "id": "pdfa-convert", "name": "PDF/A Convert", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -4091,9 +4027,7 @@ "id": "crop-pdf", "name": "Crop PDF", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -4101,9 +4035,7 @@ "id": "nup-pdf", "name": "N-up PDF", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -4111,9 +4043,7 @@ "id": "booklet-pdf", "name": "Booklet PDF", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -4121,9 +4051,7 @@ "id": "watermark-pdf", "name": "Watermark PDF", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -4131,9 +4059,7 @@ "id": "pdf-page-numbers", "name": "PDF Page Numbers", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -4141,9 +4067,7 @@ "id": "flatten-pdf", "name": "Flatten PDF", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -4151,9 +4075,7 @@ "id": "redact-pdf", "name": "Redact PDF", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -4161,9 +4083,7 @@ "id": "pdf-to-text", "name": "PDF to Text", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -4171,9 +4091,7 @@ "id": "pdf-to-word", "name": "PDF to Word", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "long", "isAI": false }, @@ -4181,9 +4099,7 @@ "id": "pdf-metadata", "name": "PDF Metadata", "modality": "document", - "acceptedInputs": [ - ".pdf" - ], + "acceptedInputs": [".pdf"], "executionHint": "fast", "isAI": false }, @@ -4191,11 +4107,7 @@ "id": "powerpoint-to-pdf", "name": "PowerPoint to PDF", "modality": "document", - "acceptedInputs": [ - ".pptx", - ".ppt", - ".odp" - ], + "acceptedInputs": [".pptx", ".ppt", ".odp"], "executionHint": "long", "isAI": false }, @@ -4203,10 +4115,7 @@ "id": "html-to-pdf", "name": "HTML to PDF", "modality": "document", - "acceptedInputs": [ - ".html", - ".htm" - ], + "acceptedInputs": [".html", ".htm"], "executionHint": "long", "isAI": false }, @@ -4214,10 +4123,7 @@ "id": "markdown-to-docx", "name": "Markdown to Word", "modality": "document", - "acceptedInputs": [ - ".md", - ".markdown" - ], + "acceptedInputs": [".md", ".markdown"], "executionHint": "fast", "isAI": false }, @@ -4225,10 +4131,7 @@ "id": "markdown-to-html", "name": "Markdown to HTML", "modality": "document", - "acceptedInputs": [ - ".md", - ".markdown" - ], + "acceptedInputs": [".md", ".markdown"], "executionHint": "fast", "isAI": false }, @@ -4236,10 +4139,7 @@ "id": "markdown-to-pdf", "name": "Markdown to PDF", "modality": "document", - "acceptedInputs": [ - ".md", - ".markdown" - ], + "acceptedInputs": [".md", ".markdown"], "executionHint": "long", "isAI": false }, @@ -4247,9 +4147,7 @@ "id": "epub-convert", "name": "Convert EPUB", "modality": "document", - "acceptedInputs": [ - ".epub" - ], + "acceptedInputs": [".epub"], "executionHint": "long", "isAI": false }, @@ -4257,12 +4155,7 @@ "id": "to-epub", "name": "Convert to EPUB", "modality": "document", - "acceptedInputs": [ - ".docx", - ".md", - ".html", - ".txt" - ], + "acceptedInputs": [".docx", ".md", ".html", ".txt"], "executionHint": "long", "isAI": false }, @@ -4270,10 +4163,7 @@ "id": "chart-maker", "name": "Chart Maker", "modality": "file", - "acceptedInputs": [ - ".csv", - ".json" - ], + "acceptedInputs": [".csv", ".json"], "executionHint": "fast", "isAI": false }, @@ -4281,11 +4171,7 @@ "id": "csv-excel", "name": "CSV to Excel", "modality": "file", - "acceptedInputs": [ - ".csv", - ".tsv", - ".xlsx" - ], + "acceptedInputs": [".csv", ".tsv", ".xlsx"], "executionHint": "fast", "isAI": false }, @@ -4293,11 +4179,7 @@ "id": "csv-json", "name": "CSV to JSON", "modality": "file", - "acceptedInputs": [ - ".csv", - ".tsv", - ".json" - ], + "acceptedInputs": [".csv", ".tsv", ".json"], "executionHint": "fast", "isAI": false }, @@ -4305,10 +4187,7 @@ "id": "json-xml", "name": "JSON to XML", "modality": "file", - "acceptedInputs": [ - ".json", - ".xml" - ], + "acceptedInputs": [".json", ".xml"], "executionHint": "fast", "isAI": false }, @@ -4316,10 +4195,7 @@ "id": "split-csv", "name": "Split CSV", "modality": "file", - "acceptedInputs": [ - ".csv", - ".tsv" - ], + "acceptedInputs": [".csv", ".tsv"], "executionHint": "fast", "isAI": false }, @@ -4327,10 +4203,7 @@ "id": "merge-csvs", "name": "Merge CSVs", "modality": "file", - "acceptedInputs": [ - ".csv", - ".tsv" - ], + "acceptedInputs": [".csv", ".tsv"], "executionHint": "fast", "isAI": false }, @@ -4338,11 +4211,7 @@ "id": "yaml-json", "name": "YAML / JSON", "modality": "file", - "acceptedInputs": [ - ".yaml", - ".yml", - ".json" - ], + "acceptedInputs": [".yaml", ".yml", ".json"], "executionHint": "fast", "isAI": false }, @@ -4350,9 +4219,7 @@ "id": "xml-to-csv", "name": "XML to CSV", "modality": "file", - "acceptedInputs": [ - ".xml" - ], + "acceptedInputs": [".xml"], "executionHint": "fast", "isAI": false }, @@ -4368,9 +4235,7 @@ "id": "extract-zip", "name": "Extract ZIP", "modality": "file", - "acceptedInputs": [ - ".zip" - ], + "acceptedInputs": [".zip"], "executionHint": "fast", "isAI": false }