feat: Update route patterns to use path-to-regexp v8 syntax (#9942)

BREAKING CHANGE: Route pattern syntax across cloud routes and rate-limiting now use the new path-to-regexp v8 syntax; see the [migration guide](https://github.com/parse-community/parse-server/blob/alpha/9.0.0.md) for more details.
This commit is contained in:
Lucas
2025-12-12 19:36:27 +01:00
committed by GitHub
parent 5a61993cb7
commit fa8723b3d1
10 changed files with 100 additions and 105 deletions

View File

@@ -3,6 +3,7 @@
// mount is the URL for the root of the API; includes http, domain, etc.
import { isBoolean, isString } from 'lodash';
import { pathToRegexp } from 'path-to-regexp';
import net from 'net';
import AppCache from './cache';
import DatabaseController from './Controllers/DatabaseController';
@@ -687,6 +688,14 @@ export class Config {
if (typeof option.requestPath !== 'string') {
throw `rateLimit.requestPath must be a string`;
}
// Validate that the path is valid path-to-regexp syntax
try {
pathToRegexp(option.requestPath);
} catch (error) {
throw `rateLimit.requestPath "${option.requestPath}" is not valid: ${error.message}`;
}
if (option.requestTimeWindow == null) {
throw `rateLimit.requestTimeWindow must be defined`;
}

View File

@@ -686,7 +686,7 @@ module.exports.RateLimitOptions = {
requestPath: {
env: 'PARSE_SERVER_RATE_LIMIT_REQUEST_PATH',
help:
'The path of the API route to be rate limited. Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings, string patterns, or regular expression. See: https://expressjs.com/en/guide/routing.html',
'The path of the API route to be rate limited. Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings or string patterns following <a href="https://github.com/pillarjs/path-to-regexp">path-to-regexp v8</a> syntax.',
required: true,
},
requestTimeWindow: {

View File

@@ -121,7 +121,7 @@
* @property {String} redisUrl Optional, the URL of the Redis server to store rate limit data. This allows to rate limit requests for multiple servers by calculating the sum of all requests across all servers. This is useful if multiple servers are processing requests behind a load balancer. For example, the limit of 10 requests is reached if each of 2 servers processed 5 requests.
* @property {Number} requestCount The number of requests that can be made per IP address within the time window set in `requestTimeWindow` before the rate limit is applied.
* @property {String[]} requestMethods Optional, the HTTP request methods to which the rate limit should be applied, default is all methods.
* @property {String} requestPath The path of the API route to be rate limited. Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings, string patterns, or regular expression. See: https://expressjs.com/en/guide/routing.html
* @property {String} requestPath The path of the API route to be rate limited. Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings or string patterns following <a href="https://github.com/pillarjs/path-to-regexp">path-to-regexp v8</a> syntax.
* @property {Number} requestTimeWindow The window of time in milliseconds within which the number of requests set in `requestCount` can be made before the rate limit is applied.
* @property {String} zone The type of rate limit to apply. The following types are supported:<ul><li>`global`: rate limit based on the number of requests made by all users</li><li>`ip`: rate limit based on the IP address of the request</li><li>`user`: rate limit based on the user ID of the request</li><li>`session`: rate limit based on the session token of the request</li></ul>Default is `ip`.
*/

View File

@@ -353,7 +353,7 @@ export interface ParseServerOptions {
}
export interface RateLimitOptions {
/* The path of the API route to be rate limited. Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings, string patterns, or regular expression. See: https://expressjs.com/en/guide/routing.html */
/* The path of the API route to be rate limited. Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings or string patterns following <a href="https://github.com/pillarjs/path-to-regexp">path-to-regexp v8</a> syntax. */
requestPath: string;
/* The window of time in milliseconds within which the number of requests set in `requestCount` can be made before the rate limit is applied. */
requestTimeWindow: ?number;

View File

@@ -82,12 +82,12 @@ const getRoute = parseClass => {
'@Config' : 'config',
}[parseClass] || 'classes';
if (parseClass === '@File') {
return `/${route}/:id?(.*)`;
return `/${route}{/*id}`;
}
if (parseClass === '@Config') {
return `/${route}`;
}
return `/${route}/${parseClass}/:id?(.*)`;
return `/${route}/${parseClass}{/*id}`;
};
/** @namespace
* @name Parse

View File

@@ -322,7 +322,7 @@ const handleRateLimit = async (req, res, next) => {
try {
await Promise.all(
rateLimits.map(async limit => {
const pathExp = new RegExp(limit.path);
const pathExp = limit.path.regexp || limit.path;
if (pathExp.test(req.url)) {
await limit.handler(req, res, err => {
if (err) {
@@ -560,12 +560,8 @@ export const addRateLimit = (route, config, cloud) => {
},
});
}
let transformPath = route.requestPath.split('/*').join('/(.*)');
if (transformPath === '*') {
transformPath = '(.*)';
}
config.rateLimits.push({
path: pathToRegexp(transformPath),
path: pathToRegexp(route.requestPath),
handler: rateLimit({
windowMs: route.requestTimeWindow,
max: route.requestCount,