diff --git a/.env.example b/.env.example index d69da90c..1e98a941 100644 --- a/.env.example +++ b/.env.example @@ -13,6 +13,8 @@ POSTGRESQL_DB=framna-docs REPOSITORY_NAME_SUFFIX=-openapi HIDDEN_REPOSITORIES= NEW_PROJECT_TEMPLATE_REPOSITORY=shapehq/starter-openapi +PROXY_API_MAXIMUM_FILE_SIZE_IN_MEGABYTES = 10 +PROXY_API_TIMEOUT_IN_SECONDS = 30 GITHUB_WEBHOOK_SECRET=preshared secret also put in app configuration in GitHub GITHUB_WEBHOK_REPOSITORY_ALLOWLIST= GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST= diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c969f85a..d350b2e1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 - name: Setup Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 20 - name: Setup Environment diff --git a/.github/workflows/run-unit-tests.yml b/.github/workflows/run-unit-tests.yml index cc89eee3..67fb0b08 100644 --- a/.github/workflows/run-unit-tests.yml +++ b/.github/workflows/run-unit-tests.yml @@ -15,7 +15,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 - name: Setup Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 20 - name: Install Dependencies diff --git a/package-lock.json b/package-lock.json index bba1ed92..79f84ab8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,13 +10,13 @@ "dependencies": { "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", - "@fontsource/poppins": "^5.0.14", + "@fontsource/poppins": "^5.1.0", "@fortawesome/fontawesome-svg-core": "^6.6.0", "@fortawesome/free-brands-svg-icons": "^6.6.0", "@fortawesome/free-regular-svg-icons": "^6.6.0", "@fortawesome/free-solid-svg-icons": "^6.6.0", "@fortawesome/react-fontawesome": "^0.2.2", - "@mui/icons-material": "^6.1.4", + "@mui/icons-material": "^6.1.5", "@mui/material": "^6.0.1", "@octokit/auth-app": "^7.1.1", "@octokit/core": "^6.1.2", @@ -50,16 +50,16 @@ "@types/jest": "^29.5.12", "@types/node": "^22.7.4", "@types/nprogress": "^0.2.3", - "@types/pg": "^8.11.6", - "@types/react": "^18.3.11", + "@types/pg": "^8.11.10", + "@types/react": "^18.3.12", "@types/react-dom": "^18.3.0", "@types/swagger-ui-react": "^4.18.3", - "@typescript-eslint/eslint-plugin": "^8.10.0", - "@typescript-eslint/parser": "^8.10.0", + "@typescript-eslint/eslint-plugin": "^8.11.0", + "@typescript-eslint/parser": "^8.11.0", "autoprefixer": "^10.4.20", "eslint": "^8.57.1", "eslint-config-next": "^14.2.14", - "pg": "^8.12.0", + "pg": "^8.13.1", "postcss": "^8.4.41", "tailwindcss": "^3.4.13", "ts-jest": "^29.2.5", @@ -934,9 +934,9 @@ "deprecated": "Please update to a newer version." }, "node_modules/@fontsource/poppins": { - "version": "5.0.14", - "resolved": "https://registry.npmjs.org/@fontsource/poppins/-/poppins-5.0.14.tgz", - "integrity": "sha512-nmM1zpPo3Uh4JcGAVSQuWaZNYh2FbbwWhZ5t6hRaynmJaNTBW85d3nEh9zMmzI0HX7X5xqQVdRHeDatKpOGsnA==" + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@fontsource/poppins/-/poppins-5.1.0.tgz", + "integrity": "sha512-tpLXlnNi2fwQjiipvuj4uNFHCdoLA8izRsKdoexZuEzjx0r/g1aKLf4ta6lFgF7L+/+AFdmaXFlUwwvmDzYH+g==" }, "node_modules/@fortawesome/fontawesome-common-types": { "version": "6.6.0", @@ -2031,18 +2031,18 @@ "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==" }, "node_modules/@mui/core-downloads-tracker": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.4.tgz", - "integrity": "sha512-jCRsB9NDJJatVCHvwWSTfYUzuTQ7E0Km6tAQWz2Md1SLHIbVj5visC9yHbf/Cv2IDcG6XdHRv3e7Bt1rIburNw==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.5.tgz", + "integrity": "sha512-3J96098GrC95XsLw/TpGNMxhUOnoG9NZ/17Pfk1CrJj+4rcuolsF2RdF3XAFTu/3a/A+5ouxlSIykzYz6Ee87g==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" } }, "node_modules/@mui/icons-material": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.1.4.tgz", - "integrity": "sha512-nhXBNSP3WkY0pz8dg25VIYIXJkhdRLRKZtD50f9OuHVQ1eh8b+enmvaZQF0o5M8cs1sR6wQHwZYwG34qDZeG0g==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.1.5.tgz", + "integrity": "sha512-SbxFtO5I4cXfvhjAMgGib/t2lQUzcEzcDFYiRHRufZUeMMeXuoKaGsptfwAHTepYkv0VqcCwvxtvtWbpZLAbjQ==", "dependencies": { "@babel/runtime": "^7.25.7" }, @@ -2054,7 +2054,7 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^6.1.4", + "@mui/material": "^6.1.5", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -2065,15 +2065,15 @@ } }, "node_modules/@mui/material": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.4.tgz", - "integrity": "sha512-mIVdjzDYU4U/XYzf8pPEz3zDZFS4Wbyr0cjfgeGiT/s60EvtEresXXQy8XUA0bpJDJjgic1Hl5AIRcqWDyi2eg==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.5.tgz", + "integrity": "sha512-rhaxC7LnlOG8zIVYv7BycNbWkC5dlm9A/tcDUp0CuwA7Zf9B9JP6M3rr50cNKxI7Z0GIUesAT86ceVm44quwnQ==", "dependencies": { "@babel/runtime": "^7.25.7", - "@mui/core-downloads-tracker": "^6.1.4", - "@mui/system": "^6.1.4", + "@mui/core-downloads-tracker": "^6.1.5", + "@mui/system": "^6.1.5", "@mui/types": "^7.2.18", - "@mui/utils": "^6.1.4", + "@mui/utils": "^6.1.5", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.11", "clsx": "^2.1.1", @@ -2092,7 +2092,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^6.1.4", + "@mui/material-pigment-css": "^6.1.5", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -2113,12 +2113,12 @@ } }, "node_modules/@mui/private-theming": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.4.tgz", - "integrity": "sha512-FPa+W5BSrRM/1QI5Gf/GwJinJ2WsrKPpJB6xMmmXMXSUIp31YioIVT04i28DQUXFFB3yZY12ukcZi51iLvPljw==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.5.tgz", + "integrity": "sha512-FJqweqEXk0KdtTho9C2h6JEKXsOT7MAVH2Uj3N5oIqs6YKxnwBn2/zL2QuYYEtj5OJ87rEUnCfFic6ldClvzJw==", "dependencies": { "@babel/runtime": "^7.25.7", - "@mui/utils": "^6.1.4", + "@mui/utils": "^6.1.5", "prop-types": "^15.8.1" }, "engines": { @@ -2139,9 +2139,9 @@ } }, "node_modules/@mui/styled-engine": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.4.tgz", - "integrity": "sha512-D+aiIDtJsU9OVJ7dgayhCDABJHT7jTlnz1FKyxa5mNVHsxjjeG1M4OpLsRQvx4dcvJfDywnU2cE+nFm4Ln2aFQ==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.5.tgz", + "integrity": "sha512-tiyWzMkHeWlOoE6AqomWvYvdml8Nv5k5T+LDwOiwHEawx8P9Lyja6ZwWPU6xljwPXYYPT2KBp1XvMly7dsK46A==", "dependencies": { "@babel/runtime": "^7.25.7", "@emotion/cache": "^11.13.1", @@ -2172,15 +2172,15 @@ } }, "node_modules/@mui/system": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.4.tgz", - "integrity": "sha512-lCveY/UtDhYwMg1WnLc3wEEuGymLi6YI79VOwFV9zfZT5Et+XEw/e1It26fiKwUZ+mB1+v1iTYMpJnwnsrn2aQ==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.5.tgz", + "integrity": "sha512-vPM9ocQ8qquRDByTG3XF/wfYTL7IWL/20EiiKqByLDps8wOmbrDG9rVznSE3ZbcjFCFfMRMhtxvN92bwe/63SA==", "dependencies": { "@babel/runtime": "^7.25.7", - "@mui/private-theming": "^6.1.4", - "@mui/styled-engine": "^6.1.4", + "@mui/private-theming": "^6.1.5", + "@mui/styled-engine": "^6.1.5", "@mui/types": "^7.2.18", - "@mui/utils": "^6.1.4", + "@mui/utils": "^6.1.5", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -2224,9 +2224,9 @@ } }, "node_modules/@mui/utils": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.4.tgz", - "integrity": "sha512-v0wXkyh3/Hpw48ivlNvgs4ZT6M8BIEAMdLgvct59rQBggYFhoAVKyliKDzdj37CnIlYau3DYIn7x5bHlRYFBow==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.5.tgz", + "integrity": "sha512-vp2WfNDY+IbKUIGg+eqX1Ry4t/BilMjzp6p9xO1rfqpYjH1mj8coQxxDfKxcQLzBQkmBJjymjoGOak5VUYwXug==", "dependencies": { "@babel/runtime": "^7.25.7", "@mui/types": "^7.2.18", @@ -4474,9 +4474,9 @@ "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==" }, "node_modules/@types/pg": { - "version": "8.11.6", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.6.tgz", - "integrity": "sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==", + "version": "8.11.10", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.10.tgz", + "integrity": "sha512-LczQUW4dbOQzsH2RQ5qoeJ6qJPdrcM/DcMLoqWQkMLMsq83J5lAX3LXjdkWdpscFy67JSOWDnh7Ny/sPFykmkg==", "dev": true, "dependencies": { "@types/node": "*", @@ -4498,9 +4498,9 @@ } }, "node_modules/@types/react": { - "version": "18.3.11", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.11.tgz", - "integrity": "sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==", + "version": "18.3.12", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", + "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -4587,16 +4587,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.10.0.tgz", - "integrity": "sha512-phuB3hoP7FFKbRXxjl+DRlQDuJqhpOnm5MmtROXyWi3uS/Xg2ZXqiQfcG2BJHiN4QKyzdOJi3NEn/qTnjUlkmQ==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.11.0.tgz", + "integrity": "sha512-KhGn2LjW1PJT2A/GfDpiyOfS4a8xHQv2myUagTM5+zsormOmBlYsnQ6pobJ8XxJmh6hnHwa2Mbe3fPrDJoDhbA==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.10.0", - "@typescript-eslint/type-utils": "8.10.0", - "@typescript-eslint/utils": "8.10.0", - "@typescript-eslint/visitor-keys": "8.10.0", + "@typescript-eslint/scope-manager": "8.11.0", + "@typescript-eslint/type-utils": "8.11.0", + "@typescript-eslint/utils": "8.11.0", + "@typescript-eslint/visitor-keys": "8.11.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -4620,15 +4620,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.10.0.tgz", - "integrity": "sha512-E24l90SxuJhytWJ0pTQydFT46Nk0Z+bsLKo/L8rtQSL93rQ6byd1V/QbDpHUTdLPOMsBCcYXZweADNCfOCmOAg==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.11.0.tgz", + "integrity": "sha512-lmt73NeHdy1Q/2ul295Qy3uninSqi6wQI18XwSpm8w0ZbQXUpjCAWP1Vlv/obudoBiIjJVjlztjQ+d/Md98Yxg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.10.0", - "@typescript-eslint/types": "8.10.0", - "@typescript-eslint/typescript-estree": "8.10.0", - "@typescript-eslint/visitor-keys": "8.10.0", + "@typescript-eslint/scope-manager": "8.11.0", + "@typescript-eslint/types": "8.11.0", + "@typescript-eslint/typescript-estree": "8.11.0", + "@typescript-eslint/visitor-keys": "8.11.0", "debug": "^4.3.4" }, "engines": { @@ -4648,13 +4648,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.10.0.tgz", - "integrity": "sha512-AgCaEjhfql9MDKjMUxWvH7HjLeBqMCBfIaBbzzIcBbQPZE7CPh1m6FF+L75NUMJFMLYhCywJXIDEMa3//1A0dw==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.11.0.tgz", + "integrity": "sha512-Uholz7tWhXmA4r6epo+vaeV7yjdKy5QFCERMjs1kMVsLRKIrSdM6o21W2He9ftp5PP6aWOVpD5zvrvuHZC0bMQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.10.0", - "@typescript-eslint/visitor-keys": "8.10.0" + "@typescript-eslint/types": "8.11.0", + "@typescript-eslint/visitor-keys": "8.11.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4665,13 +4665,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.10.0.tgz", - "integrity": "sha512-PCpUOpyQSpxBn230yIcK+LeCQaXuxrgCm2Zk1S+PTIRJsEfU6nJ0TtwyH8pIwPK/vJoA+7TZtzyAJSGBz+s/dg==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.11.0.tgz", + "integrity": "sha512-ItiMfJS6pQU0NIKAaybBKkuVzo6IdnAhPFZA/2Mba/uBjuPQPet/8+zh5GtLHwmuFRShZx+8lhIs7/QeDHflOg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.10.0", - "@typescript-eslint/utils": "8.10.0", + "@typescript-eslint/typescript-estree": "8.11.0", + "@typescript-eslint/utils": "8.11.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -4689,9 +4689,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.10.0.tgz", - "integrity": "sha512-k/E48uzsfJCRRbGLapdZgrX52csmWJ2rcowwPvOZ8lwPUv3xW6CcFeJAXgx4uJm+Ge4+a4tFOkdYvSpxhRhg1w==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.11.0.tgz", + "integrity": "sha512-tn6sNMHf6EBAYMvmPUaKaVeYvhUsrE6x+bXQTxjQRp360h1giATU0WvgeEys1spbvb5R+VpNOZ+XJmjD8wOUHw==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4702,13 +4702,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.10.0.tgz", - "integrity": "sha512-3OE0nlcOHaMvQ8Xu5gAfME3/tWVDpb/HxtpUZ1WeOAksZ/h/gwrBzCklaGzwZT97/lBbbxJ16dMA98JMEngW4w==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.11.0.tgz", + "integrity": "sha512-yHC3s1z1RCHoCz5t06gf7jH24rr3vns08XXhfEqzYpd6Hll3z/3g23JRi0jM8A47UFKNc3u/y5KIMx8Ynbjohg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.10.0", - "@typescript-eslint/visitor-keys": "8.10.0", + "@typescript-eslint/types": "8.11.0", + "@typescript-eslint/visitor-keys": "8.11.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -4730,15 +4730,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.10.0.tgz", - "integrity": "sha512-Oq4uZ7JFr9d1ZunE/QKy5egcDRXT/FrS2z/nlxzPua2VHFtmMvFNDvpq1m/hq0ra+T52aUezfcjGRIB7vNJF9w==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.11.0.tgz", + "integrity": "sha512-CYiX6WZcbXNJV7UNB4PLDIBtSdRmRI/nb0FMyqHPTQD1rMjA0foPLaPUV39C/MxkTd/QKSeX+Gb34PPsDVC35g==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.10.0", - "@typescript-eslint/types": "8.10.0", - "@typescript-eslint/typescript-estree": "8.10.0" + "@typescript-eslint/scope-manager": "8.11.0", + "@typescript-eslint/types": "8.11.0", + "@typescript-eslint/typescript-estree": "8.11.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4752,12 +4752,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.10.0.tgz", - "integrity": "sha512-k8nekgqwr7FadWk548Lfph6V3r9OVqjzAIVskE7orMZR23cGJjAOVazsZSJW+ElyjfTM4wx/1g88Mi70DDtG9A==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.11.0.tgz", + "integrity": "sha512-EaewX6lxSjRJnc+99+dqzTeoDZUfyrA52d2/HRrkI830kgovWsmIiTfmr0NZorzqic7ga+1bS60lRBUgR3n/Bw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/types": "8.11.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -16262,14 +16262,14 @@ "peer": true }, "node_modules/pg": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.12.0.tgz", - "integrity": "sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ==", + "version": "8.13.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.13.1.tgz", + "integrity": "sha512-OUir1A0rPNZlX//c7ksiu7crsGZTKSOXJPgtNiHGIlC9H0lO+NC6ZDYksSgBYY/thSWhnSRBv8w1lieNNGATNQ==", "dev": true, "dependencies": { - "pg-connection-string": "^2.6.4", - "pg-pool": "^3.6.2", - "pg-protocol": "^1.6.1", + "pg-connection-string": "^2.7.0", + "pg-pool": "^3.7.0", + "pg-protocol": "^1.7.0", "pg-types": "^2.1.0", "pgpass": "1.x" }, @@ -16296,9 +16296,9 @@ "optional": true }, "node_modules/pg-connection-string": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", - "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", + "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==", "dev": true }, "node_modules/pg-int8": { @@ -16320,18 +16320,18 @@ } }, "node_modules/pg-pool": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", - "integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.7.0.tgz", + "integrity": "sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==", "dev": true, "peerDependencies": { "pg": ">=8.0" } }, "node_modules/pg-protocol": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz", - "integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.7.0.tgz", + "integrity": "sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==", "dev": true }, "node_modules/pg-types": { diff --git a/package.json b/package.json index 60756f63..4e7921f4 100644 --- a/package.json +++ b/package.json @@ -17,13 +17,13 @@ "dependencies": { "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", - "@fontsource/poppins": "^5.0.14", + "@fontsource/poppins": "^5.1.0", "@fortawesome/fontawesome-svg-core": "^6.6.0", "@fortawesome/free-brands-svg-icons": "^6.6.0", "@fortawesome/free-regular-svg-icons": "^6.6.0", "@fortawesome/free-solid-svg-icons": "^6.6.0", "@fortawesome/react-fontawesome": "^0.2.2", - "@mui/icons-material": "^6.1.4", + "@mui/icons-material": "^6.1.5", "@mui/material": "^6.0.1", "@octokit/auth-app": "^7.1.1", "@octokit/core": "^6.1.2", @@ -57,16 +57,16 @@ "@types/jest": "^29.5.12", "@types/node": "^22.7.4", "@types/nprogress": "^0.2.3", - "@types/pg": "^8.11.6", - "@types/react": "^18.3.11", + "@types/pg": "^8.11.10", + "@types/react": "^18.3.12", "@types/react-dom": "^18.3.0", "@types/swagger-ui-react": "^4.18.3", - "@typescript-eslint/eslint-plugin": "^8.10.0", - "@typescript-eslint/parser": "^8.10.0", + "@typescript-eslint/eslint-plugin": "^8.11.0", + "@typescript-eslint/parser": "^8.11.0", "autoprefixer": "^10.4.20", "eslint": "^8.57.1", "eslint-config-next": "^14.2.14", - "pg": "^8.12.0", + "pg": "^8.13.1", "postcss": "^8.4.41", "tailwindcss": "^3.4.13", "ts-jest": "^29.2.5", diff --git a/src/app/api/proxy/route.ts b/src/app/api/proxy/route.ts index 22226634..4a6c4b9a 100644 --- a/src/app/api/proxy/route.ts +++ b/src/app/api/proxy/route.ts @@ -1,6 +1,13 @@ import { NextRequest, NextResponse } from "next/server" -import { makeAPIErrorResponse, makeUnauthenticatedAPIErrorResponse } from "@/common" +import { env, makeAPIErrorResponse, makeUnauthenticatedAPIErrorResponse } from "@/common" import { session } from "@/composition" +import { parse as parseYaml } from "yaml" + +const ErrorName = { + MAX_FILE_SIZE_EXCEEDED: "MaxFileSizeExceededError", + TIMEOUT: "TimeoutError", + NOT_JSON_OR_YAML: "NotJsonOrYamlError", +} export async function GET(req: NextRequest) { const isAuthenticated = await session.getIsAuthenticated() @@ -17,6 +24,79 @@ export async function GET(req: NextRequest) { } catch { return makeAPIErrorResponse(400, "Invalid \"url\" query parameter.") } - const file = await fetch(url).then(r => r.blob()) - return new NextResponse(file, { status: 200 }) + try { + const maxMegabytes = Number(env.getOrThrow("PROXY_API_MAXIMUM_FILE_SIZE_IN_MEGABYTES")) + const timeoutInSeconds = Number(env.getOrThrow("PROXY_API_TIMEOUT_IN_SECONDS")) + const maxBytes = maxMegabytes * 1024 * 1024 + const fileText = await downloadFile({ url, maxBytes, timeoutInSeconds }) + checkIfJsonOrYaml(fileText) + return new NextResponse(fileText, { status: 200 }) + } catch (error) { + if (error instanceof Error == false) { + return makeAPIErrorResponse(500, "An unknown error occurred.") + } + if (error.name === ErrorName.MAX_FILE_SIZE_EXCEEDED) { + return makeAPIErrorResponse(413, "The operation was aborted.") + } else if (error.name === ErrorName.TIMEOUT) { + return makeAPIErrorResponse(408, "The operation timed out.") + } else if (error.name === ErrorName.NOT_JSON_OR_YAML) { + return makeAPIErrorResponse(400, "Url does not point to a JSON or YAML file.") + } else { + return makeAPIErrorResponse(500, error.message) + } + } +} + +async function downloadFile(params: { + url: URL, + maxBytes: number, + timeoutInSeconds: number +}): Promise { + const { url, maxBytes, timeoutInSeconds } = params + const abortController = new AbortController() + const timeoutSignal = AbortSignal.timeout(timeoutInSeconds * 1000) + const response = await fetch(url, { + signal: AbortSignal.any([abortController.signal, timeoutSignal]) + }) + if (!response.body) { + throw new Error("Response body unavailable") + } + let totalBytes = 0 + let didExceedMaxBytes = false + const reader = response.body.getReader() + const chunks: Uint8Array[] = [] + // eslint-disable-next-line no-constant-condition + while (true) { + // eslint-disable-next-line no-await-in-loop + const { done, value } = await reader.read() + if (done) { + break + } + totalBytes += value.length + chunks.push(value) + if (totalBytes >= maxBytes) { + didExceedMaxBytes = true + abortController.abort() + break + } + } + if (didExceedMaxBytes) { + const error = new Error("Maximum file size exceeded") + error.name = ErrorName.MAX_FILE_SIZE_EXCEEDED + throw error + } + const blob = new Blob(chunks) + const arrayBuffer = await blob.arrayBuffer() + const decoder = new TextDecoder() + return decoder.decode(arrayBuffer) +} + +function checkIfJsonOrYaml(fileText: string) { + try { + parseYaml(fileText) // will also parse JSON as it is a subset of YAML + } catch { + const error = new Error("File is not JSON or YAML") + error.name = ErrorName.NOT_JSON_OR_YAML + throw error + } }