build: Release beta (#8525)

This commit is contained in:
Manuel
2023-05-01 23:35:02 +02:00
committed by GitHub
47 changed files with 903 additions and 170 deletions

View File

@@ -74,7 +74,7 @@ jobs:
check-definitions:
name: Check Definitions
timeout-minutes: 5
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.NODE_VERSION }}

View File

@@ -42,7 +42,7 @@ jobs:
env:
REGISTRY: docker.io
IMAGE_NAME: parseplatform/parse-server
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
@@ -86,7 +86,7 @@ jobs:
docs:
needs: release
if: needs.release.outputs.current_tag != '' && github.ref == 'refs/heads/release'
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v2

View File

@@ -14,7 +14,7 @@ env:
IMAGE_NAME: parseplatform/parse-server
jobs:
build:
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

View File

@@ -1,3 +1,66 @@
# [6.1.0-alpha.8](https://github.com/parse-community/parse-server/compare/6.1.0-alpha.7...6.1.0-alpha.8) (2023-05-01)
### Features
* Allow multiple origins for header `Access-Control-Allow-Origin` ([#8517](https://github.com/parse-community/parse-server/issues/8517)) ([4f15539](https://github.com/parse-community/parse-server/commit/4f15539ac244aa2d393ac5177f7604b43f69e271))
# [6.1.0-alpha.7](https://github.com/parse-community/parse-server/compare/6.1.0-alpha.6...6.1.0-alpha.7) (2023-03-10)
### Bug Fixes
* Rate limiting across multiple servers via Redis not working ([#8469](https://github.com/parse-community/parse-server/issues/8469)) ([d9e347d](https://github.com/parse-community/parse-server/commit/d9e347d7413f30f58ffbb8397fc8b5ae23be6ff0))
# [6.1.0-alpha.6](https://github.com/parse-community/parse-server/compare/6.1.0-alpha.5...6.1.0-alpha.6) (2023-03-06)
### Features
* Add rate limiting across multiple servers via Redis ([#8394](https://github.com/parse-community/parse-server/issues/8394)) ([34833e4](https://github.com/parse-community/parse-server/commit/34833e42eec08b812b733be78df0535ab0e096b6))
# [6.1.0-alpha.5](https://github.com/parse-community/parse-server/compare/6.1.0-alpha.4...6.1.0-alpha.5) (2023-03-06)
### Bug Fixes
* LiveQuery can return incorrectly formatted date ([#8456](https://github.com/parse-community/parse-server/issues/8456)) ([4ce135a](https://github.com/parse-community/parse-server/commit/4ce135a4fe930776044bc8fd786a4e17a0144e03))
# [6.1.0-alpha.4](https://github.com/parse-community/parse-server/compare/6.1.0-alpha.3...6.1.0-alpha.4) (2023-03-06)
### Bug Fixes
* Parameters missing in `afterFind` trigger of authentication adapters ([#8458](https://github.com/parse-community/parse-server/issues/8458)) ([ce34747](https://github.com/parse-community/parse-server/commit/ce34747e8af54cb0b6b975da38f779a5955d2d59))
# [6.1.0-alpha.3](https://github.com/parse-community/parse-server/compare/6.1.0-alpha.2...6.1.0-alpha.3) (2023-03-06)
### Features
* Add `afterFind` trigger to authentication adapters ([#8444](https://github.com/parse-community/parse-server/issues/8444)) ([c793bb8](https://github.com/parse-community/parse-server/commit/c793bb88e7485743c7ceb65fe419cde75833ff33))
# [6.1.0-alpha.2](https://github.com/parse-community/parse-server/compare/6.1.0-alpha.1...6.1.0-alpha.2) (2023-03-05)
### Bug Fixes
* Nested date is incorrectly decoded as empty object `{}` when fetching a Parse Object ([#8446](https://github.com/parse-community/parse-server/issues/8446)) ([22d2446](https://github.com/parse-community/parse-server/commit/22d2446dfea2bc339affc20535d181097e152acf))
# [6.1.0-alpha.1](https://github.com/parse-community/parse-server/compare/6.0.0...6.1.0-alpha.1) (2023-03-03)
### Bug Fixes
* Security upgrade jsonwebtoken to 9.0.0 ([#8420](https://github.com/parse-community/parse-server/issues/8420)) ([f5bfe45](https://github.com/parse-community/parse-server/commit/f5bfe4571e82b2b7440d41f3cff0d49937398164))
### Features
* Add option `schemaCacheTtl` for schema cache pulling as alternative to `enableSchemaHooks` ([#8436](https://github.com/parse-community/parse-server/issues/8436)) ([b3b76de](https://github.com/parse-community/parse-server/commit/b3b76de71b1d4265689d052e7837c38ec1fa4323))
* Add Parse Server option `resetPasswordSuccessOnInvalidEmail` to choose success or error response on password reset with invalid email ([#7551](https://github.com/parse-community/parse-server/issues/7551)) ([e5d610e](https://github.com/parse-community/parse-server/commit/e5d610e5e487ddab86409409ac3d7362aba8f59b))
* Deprecate LiveQuery `fields` option in favor of `keys` for semantic consistency ([#8388](https://github.com/parse-community/parse-server/issues/8388)) ([a49e323](https://github.com/parse-community/parse-server/commit/a49e323d5ae640bff1c6603ec37fdaddb9328dd1))
* Export `AuthAdapter` to make it available for extension with custom authentication adapters ([#8443](https://github.com/parse-community/parse-server/issues/8443)) ([40c1961](https://github.com/parse-community/parse-server/commit/40c196153b8efa12ae384c1c0092b2ed60a260d6))
# [6.0.0-alpha.35](https://github.com/parse-community/parse-server/compare/6.0.0-alpha.34...6.0.0-alpha.35) (2023-02-27)

View File

@@ -1,10 +1,16 @@
{
"plugins": ["node_modules/jsdoc-babel", "plugins/markdown"],
"babel": {
"plugins": ["@babel/plugin-transform-flow-strip-types"]
"plugins": ["@babel/plugin-transform-flow-strip-types"]
},
"source": {
"include": ["./README.md", "./src/cloud-code", "./src/Options/docs.js", "./src/ParseServer.js", "./src/Adapters"],
"include": [
"README.md",
"./src/cloud-code",
"./src/Options/docs.js",
"./src/ParseServer.js",
"./src/Adapters"
],
"excludePattern": "(^|\\/|\\\\)_"
},
"templates": {
@@ -17,7 +23,18 @@
"monospaceLinks": false
},
"opts": {
"template": "node_modules/@parse/minami",
"recurse": true
"encoding": "utf8",
"readme": "./README.md",
"recurse": true,
"template": "./node_modules/clean-jsdoc-theme",
"theme_opts": {
"default_theme": "dark",
"title": "<img src='../.github/parse-server-logo.png' class='logo'/>",
"create_style": "header, .sidebar-section-title, .sidebar-title { color: #139cee !important } .logo { margin-left : 40px; margin-right: 40px }"
}
},
"markdown": {
"hardwrap": false,
"idInHeadings": true
}
}
}

473
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "parse-server",
"version": "6.1.0-beta.1",
"version": "6.1.0-alpha.8",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "parse-server",
"version": "6.1.0-beta.1",
"version": "6.1.0-alpha.8",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@@ -41,9 +41,10 @@
"mustache": "4.2.0",
"parse": "4.0.1",
"path-to-regexp": "0.1.7",
"pg-monitor": "1.5.0",
"pg-promise": "10.12.1",
"pg-monitor": "2.0.0",
"pg-promise": "11.3.0",
"pluralize": "8.0.0",
"rate-limit-redis": "3.0.1",
"redis": "4.0.6",
"semver": "7.3.8",
"subscriptions-transport-ws": "0.11.0",
@@ -64,7 +65,6 @@
"@babel/plugin-proposal-object-rest-spread": "7.10.0",
"@babel/plugin-transform-flow-strip-types": "7.9.0",
"@babel/preset-env": "7.10.0",
"@parse/minami": "1.0.0",
"@saithodev/semantic-release-backmerge": "2.1.2",
"@semantic-release/changelog": "5.0.1",
"@semantic-release/commit-analyzer": "8.0.1",
@@ -75,6 +75,7 @@
"all-node-versions": "11.3.0",
"apollo-upload-client": "17.0.0",
"bcrypt-nodejs": "0.0.3",
"clean-jsdoc-theme": "^4.2.7",
"cross-env": "7.0.2",
"deep-diff": "1.0.2",
"eslint": "8.26.0",
@@ -594,9 +595,9 @@
}
},
"node_modules/@babel/parser": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz",
"integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==",
"version": "7.21.4",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz",
"integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==",
"bin": {
"parser": "bin/babel-parser.js"
},
@@ -2287,6 +2288,18 @@
"@jridgewell/sourcemap-codec": "1.4.14"
}
},
"node_modules/@jsdoc/salty": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz",
"integrity": "sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==",
"dev": true,
"dependencies": {
"lodash": "^4.17.21"
},
"engines": {
"node": ">=v12.0.0"
}
},
"node_modules/@napi-rs/triples": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@napi-rs/triples/-/triples-1.1.0.tgz",
@@ -2745,12 +2758,6 @@
"resolved": "https://registry.npmjs.org/@parse/fs-files-adapter/-/fs-files-adapter-1.2.2.tgz",
"integrity": "sha512-VUsVZXgt53FULqUd9xqGDW6RXes62qHXTNOeRSlS1MOemiCdtQOUGgLHgjdYQXnZ1hPLkxZKph96AluZUb953g=="
},
"node_modules/@parse/minami": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@parse/minami/-/minami-1.0.0.tgz",
"integrity": "sha512-Rw+p0WdOOypFPVJsmhyiI+Q056ZxdP2iAtObnU1DZrsvKZTf5x0B/0SjIt0hUgWp+COjqi/p17VdBU9IAD/NJg==",
"dev": true
},
"node_modules/@parse/node-apn": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/@parse/node-apn/-/node-apn-5.1.3.tgz",
@@ -4060,11 +4067,11 @@
}
},
"node_modules/assert-options": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/assert-options/-/assert-options-0.7.0.tgz",
"integrity": "sha512-7q9uNH/Dh8gFgpIIb9ja8PJEWA5AQy3xnBC8jtKs8K/gNVCr1K6kIvlm59HUyYgvM7oEDoLzGgPcGd9FqhtXEQ==",
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/assert-options/-/assert-options-0.8.0.tgz",
"integrity": "sha512-qSELrEaEz4sGwTs4Qh+swQkjiHAysC4rot21+jzXU86dJzNG+FDqBzyS3ohSoTRf4ZLA3FSwxQdiuNl5NXUtvA==",
"engines": {
"node": ">=8.0.0"
"node": ">=10.0.0"
}
},
"node_modules/assert-plus": {
@@ -4686,6 +4693,16 @@
"node": ">=6"
}
},
"node_modules/camel-case": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
"integrity": "sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==",
"dev": true,
"dependencies": {
"no-case": "^2.2.0",
"upper-case": "^1.1.1"
}
},
"node_modules/camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
@@ -4858,6 +4875,59 @@
"node": ">=0.10.0"
}
},
"node_modules/clean-css": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz",
"integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==",
"dev": true,
"dependencies": {
"source-map": "~0.6.0"
},
"engines": {
"node": ">= 4.0"
}
},
"node_modules/clean-css/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/clean-jsdoc-theme": {
"version": "4.2.7",
"resolved": "https://registry.npmjs.org/clean-jsdoc-theme/-/clean-jsdoc-theme-4.2.7.tgz",
"integrity": "sha512-yLLxwTFw7N9/toUCcFmKkwJmv4LO2pQWYNETcU3Vv+n2al2L2ohb7xWoXLjp5m3OljC9b1m+o4adzY0YH6Ep4A==",
"dev": true,
"dependencies": {
"@jsdoc/salty": "^0.2.4",
"fs-extra": "^10.1.0",
"html-minifier": "^4.0.0",
"klaw-sync": "^6.0.0",
"lodash": "^4.17.21",
"nanoid": "^3.3.4",
"showdown": "^2.1.0"
},
"peerDependencies": {
"jsdoc": ">=3.x <=4.x"
}
},
"node_modules/clean-jsdoc-theme/node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/clean-stack": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
@@ -8718,6 +8788,15 @@
"node": ">=8"
}
},
"node_modules/he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"dev": true,
"bin": {
"he": "bin/he"
}
},
"node_modules/hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
@@ -8766,6 +8845,33 @@
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
"node_modules/html-minifier": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz",
"integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==",
"dev": true,
"dependencies": {
"camel-case": "^3.0.0",
"clean-css": "^4.2.1",
"commander": "^2.19.0",
"he": "^1.2.0",
"param-case": "^2.1.1",
"relateurl": "^0.2.7",
"uglify-js": "^3.5.1"
},
"bin": {
"html-minifier": "cli.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/html-minifier/node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
},
"node_modules/htmlparser2": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
@@ -10120,6 +10226,15 @@
"graceful-fs": "^4.1.9"
}
},
"node_modules/klaw-sync": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz",
"integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.1.11"
}
},
"node_modules/kuler": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
@@ -10885,6 +11000,12 @@
"loose-envify": "cli.js"
}
},
"node_modules/lower-case": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
"integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==",
"dev": true
},
"node_modules/lowercase-keys": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
@@ -12227,6 +12348,15 @@
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
},
"node_modules/no-case": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
"integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==",
"dev": true,
"dependencies": {
"lower-case": "^1.1.1"
}
},
"node_modules/node-abort-controller": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.0.1.tgz",
@@ -15901,6 +16031,15 @@
"node": ">= 4.0.0"
}
},
"node_modules/param-case": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
"integrity": "sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==",
"dev": true,
"dependencies": {
"no-case": "^2.2.0"
}
},
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -16069,15 +16208,15 @@
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
},
"node_modules/pg": {
"version": "8.8.0",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz",
"integrity": "sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==",
"version": "8.9.0",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.9.0.tgz",
"integrity": "sha512-ZJM+qkEbtOHRuXjmvBtOgNOXOtLSbxiMiUVMgE4rV6Zwocy03RicCVvDXgx8l4Biwo8/qORUnEqn2fdQzV7KCg==",
"dependencies": {
"buffer-writer": "2.0.0",
"packet-reader": "1.0.0",
"pg-connection-string": "^2.5.0",
"pg-pool": "^3.5.2",
"pg-protocol": "^1.5.0",
"pg-protocol": "^1.6.0",
"pg-types": "^2.1.0",
"pgpass": "1.x"
},
@@ -16115,14 +16254,14 @@
}
},
"node_modules/pg-monitor": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/pg-monitor/-/pg-monitor-1.5.0.tgz",
"integrity": "sha512-Zg5RpoYaz0zyRwAQWKrRxUZgzZ+/r4McMP4vEvg+qE8765SHAB1wHZL58uAjocG4WSK/NLP/zZhUuoyurw4l6Q==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/pg-monitor/-/pg-monitor-2.0.0.tgz",
"integrity": "sha512-UqjhroM701sRrJHhXeF1OwNBGxkN9R0YgkVU8A46wWn3RwK/K7QDylChMoDxo8TmGp86CBP4ZSf+RK9vD8XyVA==",
"dependencies": {
"cli-color": "2.0.3"
},
"engines": {
"node": ">=7.6"
"node": ">=14"
}
},
"node_modules/pg-monitor/node_modules/cli-color": {
@@ -16189,23 +16328,23 @@
}
},
"node_modules/pg-promise": {
"version": "10.12.1",
"resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-10.12.1.tgz",
"integrity": "sha512-SiJkBUDGq7PNfJFJbWferodsSH+vLrhte0Q0kVgQbwlNYeKmp9Hhkr+357+5DWEuBGOHhSu1UQffSSf5HVqRtA==",
"version": "11.3.0",
"resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-11.3.0.tgz",
"integrity": "sha512-A2CYmax5gsqVAO2N0ET9oPRCPX3kpKymj9qLVK7+jszlJL6l8uJDq/DGqLpxNi5VHwK7Dmm2WNRdrwkh1xuaxQ==",
"dependencies": {
"assert-options": "0.7.0",
"pg": "8.8.0",
"assert-options": "0.8.0",
"pg": "8.9.0",
"pg-minify": "1.6.2",
"spex": "3.2.0"
},
"engines": {
"node": ">=12.0"
"node": ">=14.0"
}
},
"node_modules/pg-protocol": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz",
"integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ=="
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz",
"integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q=="
},
"node_modules/pg-types": {
"version": "2.2.0",
@@ -16746,6 +16885,17 @@
"node": ">= 0.6"
}
},
"node_modules/rate-limit-redis": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/rate-limit-redis/-/rate-limit-redis-3.0.1.tgz",
"integrity": "sha512-L6yhOUBrAZ8VEMX9DwlM3X6hfm8yq+gBO4LoOW7+JgmNq59zE7QmLz4v5VnwYPvLeSh/e7PDcrzUI3UumJw1iw==",
"engines": {
"node": ">= 14.5.0"
},
"peerDependencies": {
"express-rate-limit": "^6"
}
},
"node_modules/raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
@@ -17308,6 +17458,15 @@
"jsesc": "bin/jsesc"
}
},
"node_modules/relateurl": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
"integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==",
"dev": true,
"engines": {
"node": ">= 0.10"
}
},
"node_modules/release-zalgo": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz",
@@ -18151,6 +18310,31 @@
"node": ">=8"
}
},
"node_modules/showdown": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz",
"integrity": "sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==",
"dev": true,
"dependencies": {
"commander": "^9.0.0"
},
"bin": {
"showdown": "bin/showdown.js"
},
"funding": {
"type": "individual",
"url": "https://www.paypal.me/tiviesantos"
}
},
"node_modules/showdown/node_modules/commander": {
"version": "9.5.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
"integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
"dev": true,
"engines": {
"node": "^12.20.0 || >=14"
}
},
"node_modules/side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
@@ -19610,7 +19794,6 @@
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz",
"integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==",
"dev": true,
"optional": true,
"bin": {
"uglifyjs": "bin/uglifyjs"
},
@@ -19836,6 +20019,12 @@
"browserslist": ">= 4.21.0"
}
},
"node_modules/upper-case": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
"integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==",
"dev": true
},
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -20810,9 +20999,9 @@
}
},
"@babel/parser": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz",
"integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA=="
"version": "7.21.4",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz",
"integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw=="
},
"@babel/plugin-proposal-object-rest-spread": {
"version": "7.10.0",
@@ -22021,6 +22210,15 @@
"@jridgewell/sourcemap-codec": "1.4.14"
}
},
"@jsdoc/salty": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz",
"integrity": "sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==",
"dev": true,
"requires": {
"lodash": "^4.17.21"
}
},
"@napi-rs/triples": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@napi-rs/triples/-/triples-1.1.0.tgz",
@@ -22347,12 +22545,6 @@
"resolved": "https://registry.npmjs.org/@parse/fs-files-adapter/-/fs-files-adapter-1.2.2.tgz",
"integrity": "sha512-VUsVZXgt53FULqUd9xqGDW6RXes62qHXTNOeRSlS1MOemiCdtQOUGgLHgjdYQXnZ1hPLkxZKph96AluZUb953g=="
},
"@parse/minami": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@parse/minami/-/minami-1.0.0.tgz",
"integrity": "sha512-Rw+p0WdOOypFPVJsmhyiI+Q056ZxdP2iAtObnU1DZrsvKZTf5x0B/0SjIt0hUgWp+COjqi/p17VdBU9IAD/NJg==",
"dev": true
},
"@parse/node-apn": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/@parse/node-apn/-/node-apn-5.1.3.tgz",
@@ -23386,9 +23578,9 @@
}
},
"assert-options": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/assert-options/-/assert-options-0.7.0.tgz",
"integrity": "sha512-7q9uNH/Dh8gFgpIIb9ja8PJEWA5AQy3xnBC8jtKs8K/gNVCr1K6kIvlm59HUyYgvM7oEDoLzGgPcGd9FqhtXEQ=="
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/assert-options/-/assert-options-0.8.0.tgz",
"integrity": "sha512-qSELrEaEz4sGwTs4Qh+swQkjiHAysC4rot21+jzXU86dJzNG+FDqBzyS3ohSoTRf4ZLA3FSwxQdiuNl5NXUtvA=="
},
"assert-plus": {
"version": "1.0.0",
@@ -23865,6 +24057,16 @@
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
},
"camel-case": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
"integrity": "sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==",
"dev": true,
"requires": {
"no-case": "^2.2.0",
"upper-case": "^1.1.1"
}
},
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
@@ -23997,6 +24199,51 @@
"static-extend": "^0.1.1"
}
},
"clean-css": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz",
"integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==",
"dev": true,
"requires": {
"source-map": "~0.6.0"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
}
},
"clean-jsdoc-theme": {
"version": "4.2.7",
"resolved": "https://registry.npmjs.org/clean-jsdoc-theme/-/clean-jsdoc-theme-4.2.7.tgz",
"integrity": "sha512-yLLxwTFw7N9/toUCcFmKkwJmv4LO2pQWYNETcU3Vv+n2al2L2ohb7xWoXLjp5m3OljC9b1m+o4adzY0YH6Ep4A==",
"dev": true,
"requires": {
"@jsdoc/salty": "^0.2.4",
"fs-extra": "^10.1.0",
"html-minifier": "^4.0.0",
"klaw-sync": "^6.0.0",
"lodash": "^4.17.21",
"nanoid": "^3.3.4",
"showdown": "^2.1.0"
},
"dependencies": {
"fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"requires": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
}
}
}
},
"clean-stack": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
@@ -26989,6 +27236,12 @@
}
}
},
"he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"dev": true
},
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
@@ -27030,6 +27283,29 @@
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
"html-minifier": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz",
"integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==",
"dev": true,
"requires": {
"camel-case": "^3.0.0",
"clean-css": "^4.2.1",
"commander": "^2.19.0",
"he": "^1.2.0",
"param-case": "^2.1.1",
"relateurl": "^0.2.7",
"uglify-js": "^3.5.1"
},
"dependencies": {
"commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
}
}
},
"htmlparser2": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
@@ -28074,6 +28350,15 @@
"graceful-fs": "^4.1.9"
}
},
"klaw-sync": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz",
"integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.11"
}
},
"kuler": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
@@ -28693,6 +28978,12 @@
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"lower-case": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
"integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==",
"dev": true
},
"lowercase-keys": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
@@ -29755,6 +30046,15 @@
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
},
"no-case": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
"integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==",
"dev": true,
"requires": {
"lower-case": "^1.1.1"
}
},
"node-abort-controller": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.0.1.tgz",
@@ -32407,6 +32707,15 @@
"wcwidth": "^1.0.1"
}
},
"param-case": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
"integrity": "sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==",
"dev": true,
"requires": {
"no-case": "^2.2.0"
}
},
"parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -32524,15 +32833,15 @@
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
},
"pg": {
"version": "8.8.0",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz",
"integrity": "sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==",
"version": "8.9.0",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.9.0.tgz",
"integrity": "sha512-ZJM+qkEbtOHRuXjmvBtOgNOXOtLSbxiMiUVMgE4rV6Zwocy03RicCVvDXgx8l4Biwo8/qORUnEqn2fdQzV7KCg==",
"requires": {
"buffer-writer": "2.0.0",
"packet-reader": "1.0.0",
"pg-connection-string": "^2.5.0",
"pg-pool": "^3.5.2",
"pg-protocol": "^1.5.0",
"pg-protocol": "^1.6.0",
"pg-types": "^2.1.0",
"pgpass": "1.x"
}
@@ -32553,9 +32862,9 @@
"integrity": "sha512-1KdmFGGTP6jplJoI8MfvRlfvMiyBivMRP7/ffh4a11RUFJ7kC2J0ZHlipoKiH/1hz+DVgceon9U2qbaHpPeyPg=="
},
"pg-monitor": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/pg-monitor/-/pg-monitor-1.5.0.tgz",
"integrity": "sha512-Zg5RpoYaz0zyRwAQWKrRxUZgzZ+/r4McMP4vEvg+qE8765SHAB1wHZL58uAjocG4WSK/NLP/zZhUuoyurw4l6Q==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/pg-monitor/-/pg-monitor-2.0.0.tgz",
"integrity": "sha512-UqjhroM701sRrJHhXeF1OwNBGxkN9R0YgkVU8A46wWn3RwK/K7QDylChMoDxo8TmGp86CBP4ZSf+RK9vD8XyVA==",
"requires": {
"cli-color": "2.0.3"
},
@@ -32621,20 +32930,20 @@
"requires": {}
},
"pg-promise": {
"version": "10.12.1",
"resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-10.12.1.tgz",
"integrity": "sha512-SiJkBUDGq7PNfJFJbWferodsSH+vLrhte0Q0kVgQbwlNYeKmp9Hhkr+357+5DWEuBGOHhSu1UQffSSf5HVqRtA==",
"version": "11.3.0",
"resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-11.3.0.tgz",
"integrity": "sha512-A2CYmax5gsqVAO2N0ET9oPRCPX3kpKymj9qLVK7+jszlJL6l8uJDq/DGqLpxNi5VHwK7Dmm2WNRdrwkh1xuaxQ==",
"requires": {
"assert-options": "0.7.0",
"pg": "8.8.0",
"assert-options": "0.8.0",
"pg": "8.9.0",
"pg-minify": "1.6.2",
"spex": "3.2.0"
}
},
"pg-protocol": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz",
"integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ=="
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz",
"integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q=="
},
"pg-types": {
"version": "2.2.0",
@@ -33032,6 +33341,12 @@
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"rate-limit-redis": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/rate-limit-redis/-/rate-limit-redis-3.0.1.tgz",
"integrity": "sha512-L6yhOUBrAZ8VEMX9DwlM3X6hfm8yq+gBO4LoOW7+JgmNq59zE7QmLz4v5VnwYPvLeSh/e7PDcrzUI3UumJw1iw==",
"requires": {}
},
"raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
@@ -33491,6 +33806,12 @@
}
}
},
"relateurl": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
"integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==",
"dev": true
},
"release-zalgo": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz",
@@ -34130,6 +34451,23 @@
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
},
"showdown": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz",
"integrity": "sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==",
"dev": true,
"requires": {
"commander": "^9.0.0"
},
"dependencies": {
"commander": {
"version": "9.5.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
"integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
"dev": true
}
}
},
"side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
@@ -35288,8 +35626,7 @@
"version": "3.17.4",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz",
"integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==",
"dev": true,
"optional": true
"dev": true
},
"unbzip2-stream": {
"version": "1.4.3",
@@ -35451,6 +35788,12 @@
"picocolors": "^1.0.0"
}
},
"upper-case": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
"integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==",
"dev": true
},
"uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "parse-server",
"version": "6.1.0-beta.1",
"version": "6.1.0-alpha.8",
"description": "An express module providing a Parse-compatible API server",
"main": "lib/index.js",
"repository": {
@@ -50,9 +50,10 @@
"mustache": "4.2.0",
"parse": "4.0.1",
"path-to-regexp": "0.1.7",
"pg-monitor": "1.5.0",
"pg-promise": "10.12.1",
"pg-monitor": "2.0.0",
"pg-promise": "11.3.0",
"pluralize": "8.0.0",
"rate-limit-redis": "3.0.1",
"redis": "4.0.6",
"semver": "7.3.8",
"subscriptions-transport-ws": "0.11.0",
@@ -70,7 +71,6 @@
"@babel/plugin-proposal-object-rest-spread": "7.10.0",
"@babel/plugin-transform-flow-strip-types": "7.9.0",
"@babel/preset-env": "7.10.0",
"@parse/minami": "1.0.0",
"@saithodev/semantic-release-backmerge": "2.1.2",
"@semantic-release/changelog": "5.0.1",
"@semantic-release/commit-analyzer": "8.0.1",
@@ -81,6 +81,7 @@
"all-node-versions": "11.3.0",
"apollo-upload-client": "17.0.0",
"bcrypt-nodejs": "0.0.3",
"clean-jsdoc-theme": "4.2.7",
"cross-env": "7.0.2",
"deep-diff": "1.0.2",
"eslint": "8.26.0",

View File

@@ -161,6 +161,9 @@ function mapperFor(elt, t) {
if (type == 'NumberOrBoolean') {
return wrap(t.identifier('numberOrBooleanParser'));
}
if (type === 'StringOrStringArray') {
return wrap(t.identifier('arrayParser'));
}
return wrap(t.identifier('objectParser'));
}
}
@@ -278,6 +281,9 @@ function inject(t, list) {
const adapterType = elt.typeAnnotation.typeParameters.params[0].id.name;
type = `Adapter<${adapterType}>`;
}
if (type === 'StringOrStringArray') {
type = 'String|String[]';
}
comments += ` * @property {${type}} ${elt.name} ${elt.help}\n`;
const obj = t.objectExpression(props);
return t.objectProperty(t.stringLiteral(elt.name), obj);

View File

@@ -59,6 +59,19 @@ describe('Auth Adapter features', () => {
validateLogin: () => Promise.resolve(),
};
const modernAdapter3 = {
validateAppId: () => Promise.resolve(),
validateSetUp: () => Promise.resolve(),
validateUpdate: () => Promise.resolve(),
validateLogin: () => Promise.resolve(),
validateOptions: () => Promise.resolve(),
afterFind() {
return {
foo: 'bar',
};
},
};
const wrongAdapter = {
validateAppId: () => Promise.resolve(),
};
@@ -332,6 +345,30 @@ describe('Auth Adapter features', () => {
expect(user.getSessionToken()).toBeDefined();
});
it('should strip out authData if required', async () => {
const spy = spyOn(modernAdapter3, 'validateOptions').and.callThrough();
const afterSpy = spyOn(modernAdapter3, 'afterFind').and.callThrough();
await reconfigureServer({ auth: { modernAdapter3 } });
const user = new Parse.User();
await user.save({ authData: { modernAdapter3: { id: 'modernAdapter3Data' } } });
await user.fetch({ sessionToken: user.getSessionToken() });
const authData = user.get('authData').modernAdapter3;
expect(authData).toEqual({ foo: 'bar' });
for (const call of afterSpy.calls.all()) {
const args = call.args[0];
if (args.user) {
user._objCount = args.user._objCount;
break;
}
}
expect(afterSpy).toHaveBeenCalledWith(
{ ip: '127.0.0.1', user, master: false },
{ id: 'modernAdapter3Data' },
undefined
);
expect(spy).toHaveBeenCalled();
});
it('should throw if no triggers found', async () => {
await reconfigureServer({ auth: { wrongAdapter } });
const user = new Parse.User();

View File

@@ -45,10 +45,6 @@ describe('Idempotency', () => {
});
});
afterAll(() => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = process.env.PARSE_SERVER_TEST_TIMEOUT || 10000;
});
// Tests
it('should enforce idempotency for cloud code function', async () => {
let counter = 0;

View File

@@ -287,6 +287,35 @@ describe('middlewares', () => {
expect(headers['Access-Control-Allow-Origin']).toEqual('https://parseplatform.org/');
});
it('should support multiple origins if several are defined in allowOrigin as an array', () => {
AppCache.put(fakeReq.body._ApplicationId, {
allowOrigin: ['https://a.com', 'https://b.com', 'https://c.com'],
});
const headers = {};
const res = {
header: (key, value) => {
headers[key] = value;
},
};
const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId);
// Test with the first domain
fakeReq.headers.origin = 'https://a.com';
allowCrossDomain(fakeReq, res, () => {});
expect(headers['Access-Control-Allow-Origin']).toEqual('https://a.com');
// Test with the second domain
fakeReq.headers.origin = 'https://b.com';
allowCrossDomain(fakeReq, res, () => {});
expect(headers['Access-Control-Allow-Origin']).toEqual('https://b.com');
// Test with the third domain
fakeReq.headers.origin = 'https://c.com';
allowCrossDomain(fakeReq, res, () => {});
expect(headers['Access-Control-Allow-Origin']).toEqual('https://c.com');
// Test with an unauthorized domain
fakeReq.headers.origin = 'https://unauthorized.com';
allowCrossDomain(fakeReq, res, () => {});
expect(headers['Access-Control-Allow-Origin']).toEqual('https://a.com');
});
it('should use user provided on field userFromJWT', done => {
AppCache.put(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',

View File

@@ -248,6 +248,10 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
expect(object.date[0] instanceof Date).toBeTrue();
expect(object.bar.date[0] instanceof Date).toBeTrue();
expect(object.foo.test.date[0] instanceof Date).toBeTrue();
const obj = await new Parse.Query('MyClass').first({ useMasterKey: true });
expect(obj.get('date')[0] instanceof Date).toBeTrue();
expect(obj.get('bar').date[0] instanceof Date).toBeTrue();
expect(obj.get('foo').test.date[0] instanceof Date).toBeTrue();
});
it('handles updating a single object with array, object date', done => {

View File

@@ -6,6 +6,7 @@ if (process.env.PARSE_SERVER_TEST_CACHE === 'redis') {
});
it('can connect', async () => {
await reconfigureServer({
appId: 'redis_live_query',
startLiveQueryServer: true,
liveQuery: {
classNames: ['TestObject'],
@@ -36,6 +37,7 @@ if (process.env.PARSE_SERVER_TEST_CACHE === 'redis') {
it('can call connect twice', async () => {
const server = await reconfigureServer({
appId: 'redis_live_query',
startLiveQueryServer: true,
liveQuery: {
classNames: ['TestObject'],

View File

@@ -115,6 +115,7 @@ describe('ParseLiveQueryServer', function () {
});
describe_only_db('mongo')('initialization', () => {
beforeEach(() => reconfigureServer({ appId: 'mongo_init_test' }));
it('can be initialized through ParseServer without liveQueryServerOptions', async () => {
const parseServer = await ParseServer.startApp({
appId: 'hello',
@@ -754,6 +755,49 @@ describe('ParseLiveQueryServer', function () {
parseLiveQueryServer._onAfterSave(message);
});
it('sends correct object for dates', async () => {
jasmine.restoreLibrary('../lib/LiveQuery/QueryTools', 'matchesQuery');
const parseLiveQueryServer = new ParseLiveQueryServer({});
const date = new Date();
const message = {
currentParseObject: {
date: { __type: 'Date', iso: date.toISOString() },
__type: 'Object',
key: 'value',
className: testClassName,
},
};
// Add mock client
const clientId = 1;
const client = addMockClient(parseLiveQueryServer, clientId);
const requestId2 = 2;
await addMockSubscription(parseLiveQueryServer, clientId, requestId2);
parseLiveQueryServer._matchesACL = function () {
return Promise.resolve(true);
};
parseLiveQueryServer._inflateParseObject(message);
parseLiveQueryServer._onAfterSave(message);
// Make sure we send leave and enter command to client
await timeout();
expect(client.pushCreate).toHaveBeenCalledWith(
requestId2,
{
className: 'TestObject',
key: 'value',
date: { __type: 'Date', iso: date.toISOString() },
},
null
);
});
it('can handle object save command which does not match any subscription', async done => {
const parseLiveQueryServer = new ParseLiveQueryServer({});
// Make mock request message
@@ -1138,8 +1182,7 @@ describe('ParseLiveQueryServer', function () {
expect(toSend.original).toBeUndefined();
expect(spy).toHaveBeenCalledWith({
usage: 'Subscribing using fields parameter',
solution:
`Subscribe using "keys" instead.`,
solution: `Subscribe using "keys" instead.`,
});
});
@@ -1945,6 +1988,7 @@ describe('ParseLiveQueryServer', function () {
} else {
subscription.clientRequestIds = new Map([[clientId, [requestId]]]);
}
subscription.query = query.where;
return subscription;
}

View File

@@ -27,13 +27,13 @@ const baseURI = 'postgres://username:password@localhost:5432/db-name';
const testfile = fs.readFileSync('./Dockerfile').toString();
const dbOptionsTest = {};
dbOptionsTest[
`${baseURI}?ssl=true&binary=true&application_name=app_name&fallback_application_name=f_app_name&poolSize=10`
`${baseURI}?ssl=true&binary=true&application_name=app_name&fallback_application_name=f_app_name&poolSize=12`
] = {
ssl: true,
binary: true,
application_name: 'app_name',
fallback_application_name: 'f_app_name',
poolSize: 10,
max: 12,
};
dbOptionsTest[`${baseURI}?ssl=&binary=aa`] = {
binary: false,
@@ -83,6 +83,20 @@ describe('PostgresConfigParser.getDatabaseOptionsFromURI', () => {
it('sets the poolSize to 10 if the it is not a number', () => {
const result = parser.getDatabaseOptionsFromURI(`${baseURI}?poolSize=sdf`);
expect(result.poolSize).toEqual(10);
expect(result.max).toEqual(10);
});
it('sets the max to 10 if the it is not a number', () => {
const result = parser.getDatabaseOptionsFromURI(`${baseURI}?&max=sdf`);
expect(result.poolSize).toBeUndefined();
expect(result.max).toEqual(10);
});
it('max should take precedence over poolSize', () => {
const result = parser.getDatabaseOptionsFromURI(`${baseURI}?poolSize=20&max=12`);
expect(result.poolSize).toBeUndefined();
expect(result.max).toEqual(12);
});
});

View File

@@ -1,3 +1,4 @@
const RedisCacheAdapter = require('../lib/Adapters/Cache/RedisCacheAdapter').default;
describe('rate limit', () => {
it('can limit cloud functions', async () => {
Parse.Cloud.define('test', () => 'Abc');
@@ -388,4 +389,33 @@ describe('rate limit', () => {
})
).toBeRejectedWith(`Invalid rate limit option "path"`);
});
describe_only(() => {
return process.env.PARSE_SERVER_TEST_CACHE === 'redis';
})('with RedisCache', function () {
it('does work with cache', async () => {
await reconfigureServer({
rateLimit: [
{
requestPath: '/classes/*',
requestTimeWindow: 10000,
requestCount: 1,
errorResponseMessage: 'Too many requests',
includeInternalRequests: true,
redisUrl: 'redis://localhost:6379',
},
],
});
const obj = new Parse.Object('Test');
await obj.save();
await expectAsync(obj.save()).toBeRejectedWith(
new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests')
);
const cache = new RedisCacheAdapter();
await cache.connect();
const value = await cache.get('rl:127.0.0.1');
expect(value).toEqual(2);
const ttl = await cache.client.ttl('rl:127.0.0.1');
expect(ttl).toEqual(10);
});
});
});

View File

@@ -198,6 +198,10 @@ beforeAll(async () => {
Parse.serverURL = 'http://localhost:' + port + '/1';
});
beforeEach(() => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = process.env.PARSE_SERVER_TEST_TIMEOUT || 10000;
});
afterEach(function (done) {
const afterLogOut = async () => {
if (Object.keys(openConnections).length > 0) {
@@ -214,6 +218,7 @@ afterEach(function (done) {
done();
};
Parse.Cloud._removeAllHooks();
Parse.CoreManager.getLiveQueryController().setDefaultLiveQueryClient();
defaults.protectedFields = { _User: { '*': ['email'] } };
databaseAdapter
.getAllClasses()

View File

@@ -558,7 +558,7 @@ describe('server', () => {
});
it('can get starting state', async () => {
await reconfigureServer({ appId: 'test2', silent: false });
await reconfigureServer({ appId: 'test2' });
const parseServer = new ParseServer.ParseServer({
...defaultConfiguration,
appId: 'test2',

View File

@@ -1,9 +1,7 @@
/*eslint no-unused-vars: "off"*/
/**
* @module Adapters
*/
/**
* @interface AnalyticsAdapter
* @module Adapters
*/
export class AnalyticsAdapter {
/**

View File

@@ -93,6 +93,24 @@ export class AuthAdapter {
challenge(challengeData, authData, options, request) {
return Promise.resolve({});
}
/**
* Triggered when auth data is fetched
* @param {Object} authData authData
* @param {Object} options additional adapter options
* @returns {Promise<Object>} Any overrides required to authData
*/
afterFind(authData, options) {
return Promise.resolve({});
}
/**
* Triggered when the adapter is first attached to Parse Server
* @param {Object} options Adapter Options
*/
validateOptions(options) {
/* */
}
}
export default AuthAdapter;

View File

@@ -1,5 +1,6 @@
import loadAdapter from '../AdapterLoader';
import Parse from 'parse/node';
import AuthAdapter from './AuthAdapter';
const apple = require('./apple');
const gcenter = require('./gcenter');
@@ -153,28 +154,46 @@ function loadAuthAdapter(provider, authOptions) {
return;
}
const adapter = Object.assign({}, defaultAdapter);
const adapter =
defaultAdapter instanceof AuthAdapter ? defaultAdapter : Object.assign({}, defaultAdapter);
const keys = [
'validateAuthData',
'validateAppId',
'validateSetUp',
'validateLogin',
'validateUpdate',
'challenge',
'validateOptions',
'policy',
'afterFind',
];
const defaultAuthAdapter = new AuthAdapter();
keys.forEach(key => {
const existing = adapter?.[key];
if (
existing &&
typeof existing === 'function' &&
existing.toString() === defaultAuthAdapter[key].toString()
) {
adapter[key] = null;
}
});
const appIds = providerOptions ? providerOptions.appIds : undefined;
// Try the configuration methods
if (providerOptions) {
const optionalAdapter = loadAdapter(providerOptions, undefined, providerOptions);
if (optionalAdapter) {
[
'validateAuthData',
'validateAppId',
'validateSetUp',
'validateLogin',
'validateUpdate',
'challenge',
'policy',
].forEach(key => {
keys.forEach(key => {
if (optionalAdapter[key]) {
adapter[key] = optionalAdapter[key];
}
});
}
}
if (adapter.validateOptions) {
adapter.validateOptions(providerOptions);
}
return { adapter, appIds, providerOptions };
}
@@ -195,9 +214,40 @@ module.exports = function (authOptions = {}, enableAnonymousUsers = true) {
return { validator: authDataValidator(provider, adapter, appIds, providerOptions), adapter };
};
const runAfterFind = async (req, authData) => {
if (!authData) {
return;
}
const adapters = Object.keys(authData);
await Promise.all(
adapters.map(async provider => {
const authAdapter = getValidatorForProvider(provider);
if (!authAdapter) {
return;
}
const {
adapter: { afterFind },
providerOptions,
} = authAdapter;
if (afterFind && typeof afterFind === 'function') {
const requestObject = {
ip: req.config.ip,
user: req.auth.user,
master: req.auth.isMaster,
};
const result = afterFind(requestObject, authData[provider], providerOptions);
if (result) {
authData[provider] = result;
}
}
})
);
};
return Object.freeze({
getValidatorForProvider,
setEnableAnonymousUsers,
runAfterFind,
});
};

View File

@@ -1,9 +1,7 @@
/*eslint no-unused-vars: "off"*/
/**
* @module Adapters
*/
/**
* @interface CacheAdapter
* @interface
* @memberof module:Adapters
*/
export class CacheAdapter {
/**

View File

@@ -1,9 +1,7 @@
/*eslint no-unused-vars: "off"*/
/**
* @module Adapters
*/
/**
* @interface MailAdapter
* @interface
* @memberof module:Adapters
* Mail Adapter prototype
* A MailAdapter should implement at least sendMail()
*/

View File

@@ -19,10 +19,8 @@
import type { Config } from '../../Config';
import Parse from 'parse/node';
/**
* @module Adapters
*/
/**
* @interface FilesAdapter
* @interface
* @memberof module:Adapters
*/
export class FilesAdapter {
/** Responsible for storing the file in order to be retrieved later by its filename

View File

@@ -1,9 +1,7 @@
/*eslint no-unused-vars: "off"*/
/**
* @module Adapters
*/
/**
* @interface LoggerAdapter
* @interface
* @memberof module:Adapters
* Logger Adapter
* Allows you to change the logger mechanism
* Default is WinstonLoggerAdapter.js

View File

@@ -1,9 +1,7 @@
/*eslint no-unused-vars: "off"*/
/**
* @module Adapters
*/
/**
* @interface PubSubAdapter
* @interface
* @memberof module:Adapters
*/
export class PubSubAdapter {
/**

View File

@@ -12,10 +12,8 @@
// android push and APNS for ios push.
/**
* @module Adapters
*/
/**
* @interface PushAdapter
* @interface
* @memberof module:Adapters
*/
export class PushAdapter {
/**

View File

@@ -188,6 +188,16 @@ const transformInteriorValue = restValue => {
// Handle atomic values
var value = transformInteriorAtom(restValue);
if (value !== CannotTransform) {
if (value && typeof value === 'object') {
if (value instanceof Date) {
return value;
}
if (value instanceof Array) {
value = value.map(transformInteriorValue);
} else {
value = mapValues(value, transformInteriorValue);
}
}
return value;
}
@@ -1014,9 +1024,6 @@ function mapValues(object, iterator) {
const result = {};
Object.keys(object).forEach(key => {
result[key] = iterator(object[key]);
if (result[key] && JSON.stringify(result[key]).includes(`"__type"`)) {
result[key] = mapValues(object[key], iterator);
}
});
return result;
}

View File

@@ -58,7 +58,7 @@ function getDatabaseOptionsFromURI(uri) {
databaseOptions.fallback_application_name = queryParams.fallback_application_name;
if (queryParams.poolSize) {
databaseOptions.poolSize = parseInt(queryParams.poolSize) || 10;
databaseOptions.max = parseInt(queryParams.poolSize) || 10;
}
if (queryParams.max) {
databaseOptions.max = parseInt(queryParams.max) || 10;

View File

@@ -11,10 +11,8 @@
// Default is WSAdapter. The above functions will be binded.
/**
* @module Adapters
*/
/**
* @interface WSSAdapter
* @interface
* @memberof module:Adapters
*/
export class WSSAdapter {
/**

View File

@@ -626,6 +626,16 @@ export class Config {
return new Date(now.getTime() + this.sessionLength * 1000);
}
unregisterRateLimiters() {
let i = this.rateLimits?.length;
while (i--) {
const limit = this.rateLimits[i];
if (limit.cloud) {
this.rateLimits.splice(i, 1);
}
}
}
get invalidLinkURL() {
return this.customPages.invalidLink || `${this.publicServerURL}/apps/invalid_link.html`;
}

View File

@@ -24,6 +24,7 @@ import UserRouter from '../Routers/UsersRouter';
import DatabaseController from '../Controllers/DatabaseController';
import { isDeepStrictEqual } from 'util';
import Deprecator from '../Deprecator/Deprecator';
import deepcopy from 'deepcopy';
class ParseLiveQueryServer {
clients: Map;
@@ -496,7 +497,7 @@ class ParseLiveQueryServer {
if (!parseObject) {
return false;
}
return matchesQuery(parseObject, subscription.query);
return matchesQuery(deepcopy(parseObject), subscription.query);
}
async _clearCachedRoles(userId: string) {

View File

@@ -81,7 +81,9 @@ module.exports.ParseServerOptions = {
},
allowOrigin: {
env: 'PARSE_SERVER_ALLOW_ORIGIN',
help: 'Sets the origin to Access-Control-Allow-Origin',
help:
'Sets origins for Access-Control-Allow-Origin. This can be a string for a single origin or an array of strings for multiple origins.',
action: parsers.arrayParser,
},
analyticsAdapter: {
env: 'PARSE_SERVER_ANALYTICS_ADAPTER',
@@ -557,6 +559,11 @@ module.exports.RateLimitOptions = {
action: parsers.booleanParser,
default: false,
},
redisUrl: {
env: 'PARSE_SERVER_RATE_LIMIT_REDIS_URL',
help:
'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.',
},
requestCount: {
env: 'PARSE_SERVER_RATE_LIMIT_REQUEST_COUNT',
help:

View File

@@ -16,7 +16,7 @@
* @property {Boolean} allowCustomObjectId Enable (or disable) custom objectId
* @property {Boolean} allowExpiredAuthDataToken Allow a user to log in even if the 3rd party authentication token that was used to sign in to their account has expired. If this is set to `false`, then the token will be validated every time the user signs in to their account. This refers to the token that is stored in the `_User.authData` field. Defaults to `true`.
* @property {String[]} allowHeaders Add headers to Access-Control-Allow-Headers
* @property {String} allowOrigin Sets the origin to Access-Control-Allow-Origin
* @property {String|String[]} allowOrigin Sets origins for Access-Control-Allow-Origin. This can be a string for a single origin or an array of strings for multiple origins.
* @property {Adapter<AnalyticsAdapter>} analyticsAdapter Adapter module for the analytics
* @property {String} appId Your Parse Application ID
* @property {String} appName Sets the app name
@@ -104,6 +104,7 @@
* @property {String} errorResponseMessage The error message that should be returned in the body of the HTTP 429 response when the rate limit is hit. Default is `Too many requests.`.
* @property {Boolean} includeInternalRequests Optional, if `true` the rate limit will also apply to requests that are made in by Cloud Code, default is `false`. Note that a public Cloud Code function that triggers internal requests may circumvent rate limiting and be vulnerable to attacks.
* @property {Boolean} includeMasterKey Optional, if `true` the rate limit will also apply to requests using the `masterKey`, default is `false`. Note that a public Cloud Code function that triggers internal requests using the `masterKey` may circumvent rate limiting and be vulnerable to attacks.
* @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

View File

@@ -35,6 +35,7 @@ type Adapter<T> = string | any | T;
type NumberOrBoolean = number | boolean;
type NumberOrString = number | string;
type ProtectedFields = any;
type StringOrStringArray = string | string[];
type RequestKeywordDenylist = {
key: string | any,
value: any,
@@ -61,8 +62,8 @@ export interface ParseServerOptions {
appName: ?string;
/* Add headers to Access-Control-Allow-Headers */
allowHeaders: ?(string[]);
/* Sets the origin to Access-Control-Allow-Origin */
allowOrigin: ?string;
/* Sets origins for Access-Control-Allow-Origin. This can be a string for a single origin or an array of strings for multiple origins. */
allowOrigin: ?StringOrStringArray;
/* Adapter module for the analytics */
analyticsAdapter: ?Adapter<AnalyticsAdapter>;
/* Adapter module for the files sub-system */
@@ -320,6 +321,9 @@ export interface RateLimitOptions {
/* Optional, if `true` the rate limit will also apply to requests that are made in by Cloud Code, default is `false`. Note that a public Cloud Code function that triggers internal requests may circumvent rate limiting and be vulnerable to attacks.
:DEFAULT: false */
includeInternalRequests: ?boolean;
/* 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.
*/
redisUrl: ?string;
}
export interface SecurityOptions {

View File

@@ -223,6 +223,9 @@ RestQuery.prototype.execute = function (executeOptions) {
.then(() => {
return this.runAfterFindTrigger();
})
.then(() => {
return this.handleAuthAdapters();
})
.then(() => {
return this.response;
});
@@ -842,6 +845,20 @@ RestQuery.prototype.runAfterFindTrigger = function () {
});
};
RestQuery.prototype.handleAuthAdapters = async function () {
if (this.className !== '_User' || this.findOptions.explain) {
return;
}
await Promise.all(
this.response.results.map(result =>
this.config.authDataManager.runAfterFind(
{ config: this.config, auth: this.auth },
result.authData
)
)
);
};
// Adds included values to the response.
// Path is a list of field names.
// Returns a promise for an augmented response.

View File

@@ -292,6 +292,7 @@ export class UsersRouter extends ClassesRouter {
if (authDataResponse) {
user.authDataResponse = authDataResponse;
}
await req.config.authDataManager.runAfterFind(req, user.authData);
return { response: user };
}

View File

@@ -7,7 +7,7 @@ import { isFunction, isString } from 'lodash';
/**
* A security check.
* @class Check
* @class
*/
class Check {
/**

View File

@@ -1,10 +1,7 @@
/**
* @module SecurityCheck
*/
/**
* A group of security checks.
* @interface CheckGroup
* @interface
* @memberof module:SecurityCheck
*/
class CheckGroup {
constructor() {

View File

@@ -1,7 +1,3 @@
/**
* @module SecurityCheck
*/
import { Check } from '../Check';
import CheckGroup from '../CheckGroup';
import Config from '../../Config';
@@ -9,7 +5,8 @@ import Parse from 'parse/node';
/**
* The security checks group for Parse Server configuration.
* Checks common Parse Server parameters such as access keys.
* Checks common Parse Server parameters such as access keys
* @memberof module:SecurityCheck
*/
class CheckGroupDatabase extends CheckGroup {
setName() {

View File

@@ -1,7 +1,3 @@
/**
* @module SecurityCheck
*/
import { Check } from '../Check';
import CheckGroup from '../CheckGroup';
import Config from '../../Config';
@@ -10,6 +6,7 @@ import Parse from 'parse/node';
/**
* The security checks group for Parse Server configuration.
* Checks common Parse Server parameters such as access keys.
* @memberof module:SecurityCheck
*/
class CheckGroupServerConfig extends CheckGroup {
setName() {

View File

@@ -1,5 +1,5 @@
/**
* @module SecurityCheck
* @memberof module:SecurityCheck
*/
/**

View File

@@ -1,7 +1,3 @@
/**
* @module SecurityCheck
*/
import Utils from '../Utils';
import { CheckState } from './Check';
import * as CheckGroups from './CheckGroups/CheckGroups';
@@ -10,6 +6,7 @@ import { isArray, isBoolean } from 'lodash';
/**
* The security check runner.
* @memberof module:SecurityCheck
*/
class CheckRunner {
/**

View File

@@ -1,4 +1,5 @@
import AppCache from './cache';
import SchemaCache from './Adapters/Cache/SchemaCache';
/**
* Destroys all data in the database
@@ -11,11 +12,17 @@ export function destroyAllDataPermanently(fast) {
return Promise.all(
Object.keys(AppCache.cache).map(appId => {
const app = AppCache.get(appId);
if (app.databaseController) {
return app.databaseController.deleteEverything(fast);
} else {
return Promise.resolve();
const deletePromises = [];
if (app.cacheAdapter) {
deletePromises.push(app.cacheAdapter.clear());
}
if (app.databaseController) {
deletePromises.push(app.databaseController.deleteEverything(fast));
} else if (app.databaseAdapter) {
SchemaCache.clear();
deletePromises.push(app.databaseAdapter.deleteAllClasses(fast));
}
return Promise.all(deletePromises);
})
);
}

View File

@@ -128,7 +128,8 @@ ParseCloud.define = function (functionName, handler, validationHandler) {
if (validationHandler && validationHandler.rateLimit) {
addRateLimit(
{ requestPath: `/functions/${functionName}`, ...validationHandler.rateLimit },
Parse.applicationId
Parse.applicationId,
true
);
}
};
@@ -191,7 +192,8 @@ ParseCloud.beforeSave = function (parseClass, handler, validationHandler) {
requestMethods: ['POST', 'PUT'],
...validationHandler.rateLimit,
},
Parse.applicationId
Parse.applicationId,
true
);
}
};
@@ -237,7 +239,8 @@ ParseCloud.beforeDelete = function (parseClass, handler, validationHandler) {
requestMethods: 'DELETE',
...validationHandler.rateLimit,
},
Parse.applicationId
Parse.applicationId,
true
);
}
};
@@ -278,7 +281,8 @@ ParseCloud.beforeLogin = function (handler, validationHandler) {
if (validationHandler && validationHandler.rateLimit) {
addRateLimit(
{ requestPath: `/login`, requestMethods: 'POST', ...validationHandler.rateLimit },
Parse.applicationId
Parse.applicationId,
true
);
}
};
@@ -456,7 +460,8 @@ ParseCloud.beforeFind = function (parseClass, handler, validationHandler) {
requestMethods: 'GET',
...validationHandler.rateLimit,
},
Parse.applicationId
Parse.applicationId,
true
);
}
};
@@ -761,6 +766,8 @@ ParseCloud.afterLiveQueryEvent = function (parseClass, handler, validationHandle
ParseCloud._removeAllHooks = () => {
triggers._unregisterAll();
const config = Config.get(Parse.applicationId);
config?.unregisterRateLimiters();
};
ParseCloud.useMasterKey = () => {

View File

@@ -6,6 +6,7 @@ import RedisCacheAdapter from './Adapters/Cache/RedisCacheAdapter';
import LRUCacheAdapter from './Adapters/Cache/LRUCache.js';
import * as TestUtils from './TestUtils';
import * as SchemaMigrations from './SchemaMigrations/Migrations';
import AuthAdapter from './Adapters/Auth/AuthAdapter';
import { useExternal } from './deprecated';
import { getLogger } from './logger';
@@ -43,4 +44,5 @@ export {
ParseGraphQLServer,
_ParseServer as ParseServer,
SchemaMigrations,
AuthAdapter,
};

View File

@@ -11,6 +11,8 @@ import rateLimit from 'express-rate-limit';
import { RateLimitOptions } from './Options/Definitions';
import pathToRegexp from 'path-to-regexp';
import ipRangeCheck from 'ip-range-check';
import RedisStore from 'rate-limit-redis';
import { createClient } from 'redis';
export const DEFAULT_ALLOWED_HEADERS =
'X-Parse-Master-Key, X-Parse-REST-API-Key, X-Parse-Javascript-Key, X-Parse-Application-Id, X-Parse-Client-Version, X-Parse-Session-Token, X-Requested-With, X-Parse-Revocable-Session, X-Parse-Request-Id, Content-Type, Pragma, Cache-Control';
@@ -382,8 +384,13 @@ export function allowCrossDomain(appId) {
if (config && config.allowHeaders) {
allowHeaders += `, ${config.allowHeaders.join(', ')}`;
}
const allowOrigin = (config && config.allowOrigin) || '*';
res.header('Access-Control-Allow-Origin', allowOrigin);
const baseOrigins =
typeof config?.allowOrigin === 'string' ? [config.allowOrigin] : config?.allowOrigin ?? ['*'];
const requestOrigin = req.headers.origin;
const allowOrigins =
requestOrigin && baseOrigins.includes(requestOrigin) ? requestOrigin : baseOrigins[0];
res.header('Access-Control-Allow-Origin', allowOrigins);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', allowHeaders);
res.header('Access-Control-Expose-Headers', 'X-Parse-Job-Status-Id, X-Parse-Push-Status-Id');
@@ -464,7 +471,7 @@ export function promiseEnforceMasterKeyAccess(request) {
return Promise.resolve();
}
export const addRateLimit = (route, config) => {
export const addRateLimit = (route, config, cloud) => {
if (typeof config === 'string') {
config = Config.get(config);
}
@@ -476,6 +483,35 @@ export const addRateLimit = (route, config) => {
if (!config.rateLimits) {
config.rateLimits = [];
}
const redisStore = {
connectionPromise: Promise.resolve(),
store: null,
connected: false,
};
if (route.redisUrl) {
const client = createClient({
url: route.redisUrl,
});
redisStore.connectionPromise = async () => {
if (redisStore.connected) {
return;
}
try {
await client.connect();
redisStore.connected = true;
} catch (e) {
const log = config?.loggerController || defaultLogger;
log.error(`Could not connect to redisURL in rate limit: ${e}`);
}
};
redisStore.connectionPromise();
redisStore.store = new RedisStore({
sendCommand: async (...args) => {
await redisStore.connectionPromise();
return client.sendCommand(args);
},
});
}
config.rateLimits.push({
path: pathToRegexp(route.requestPath),
handler: rateLimit({
@@ -512,7 +548,9 @@ export const addRateLimit = (route, config) => {
keyGenerator: request => {
return request.config.ip;
},
store: redisStore.store,
}),
cloud,
});
Config.put(config);
};