diff --git a/.env.development b/.env.development new file mode 100644 index 00000000..d3c376d7 --- /dev/null +++ b/.env.development @@ -0,0 +1,2 @@ +VITE_API_URI=http://localhost:8080 +VITE_SERVER_URI=http://localhost:8000 \ No newline at end of file diff --git a/.env.production b/.env.production new file mode 100644 index 00000000..2a19752c --- /dev/null +++ b/.env.production @@ -0,0 +1 @@ +VITE_API_URI= \ No newline at end of file diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index 47581cac..00000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,40 +0,0 @@ -module.exports = { - settings: { - react: { - version: 'detect', - }, - }, - env: { - browser: true, - commonjs: true, - es2021: true, - node: true, - }, - extends: ['plugin:react/recommended', 'prettier', 'google'], - parser: '@babel/eslint-parser', - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - babelOptions: { - presets: ['@babel/preset-react'], - }, - ecmaVersion: 2020, - sourceType: 'module', - requireConfigFile: false, - }, - plugins: ['prettier', 'react'], - rules: { - 'prettier/prettier': ['error', { singleQuote: true }], - 'object-curly-spacing': ['error', 'always'], - 'space-before-function-paren': 'off', - 'operator-linebreak': 'off', - 'quote-props': [ - 'error', - 'as-needed', - { keywords: false, unnecessary: true, numbers: false }, - ], - indent: 'off', - }, - ignorePatterns: ['build/'], -}; diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..f2d9db8e --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,41 @@ +{ + "parser": "@babel/eslint-parser", + "env": { + "node": true, + "browser": true, + "commonjs": true, + "es2021": true, + "mocha": true + }, + "extends": [ + "eslint:recommended", + "plugin:react/recommended", + "google", + "prettier", + "plugin:json/recommended" + ], + "overrides": [], + "parserOptions": { + "requireConfigFile": false, + "ecmaVersion": 12, + "sourceType": "module", + "ecmaFeatures": { + "jsx": true, + "modules": true + }, + "babelOptions": { + "presets": ["@babel/preset-react"] + } + }, + "plugins": ["react", "prettier"], + "rules": { + "react/prop-types": "off", + "require-jsdoc": "off", + "no-async-promise-executor": "off" + }, + "settings": { + "react": { + "version": "detect" + } + } +} diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..ae155646 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,10 @@ +{ + "semi": true, + "tabWidth": 2, + "printWidth": 100, + "singleQuote": true, + "trailingComma": "all", + "jsxSingleQuote": true, + "bracketSpacing": true, + "arrowParens": "always" +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 996ca341..90d17361 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,15 +1,12 @@ { - "javascript.suggestionActions.enabled": false, - "editor.tabSize": 2, - "files.eol": "\n", - "terminal.integrated.disableLineWrapping": true, - "debug.console.wordWrap": false, - "editor.wordWrap": "off", - "editor.codeActionsOnSave": { - "source.fixAll": true - }, - "eslint.validate": [ - "javascript", - "javascriptreact", - ] -} \ No newline at end of file + "javascript.suggestionActions.enabled": false, + "editor.tabSize": 2, + "files.eol": "\n", + "debug.console.wordWrap": false, + "editor.wordWrap": "off", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true +} diff --git a/config.schema.json b/config.schema.json index 9784f397..7a8ea1ad 100644 --- a/config.schema.json +++ b/config.schema.json @@ -5,6 +5,37 @@ "description": "Configuration for customizing git-proxy", "type": "object", "properties": { + "proxyUrl": { "type": "string" }, + "cookieSecret": { "type": "string" }, + "sessionMaxAgeHours": { "type": "number" }, + "api": { + "description": "Third party APIs", + "type": "object" + }, + "commitConfig": { + "description": "Enforce rules and patterns on commits including e-mail and message", + "type": "object" + }, + "attestationConfig": { + "description": "Customisable questions to add to attestation form", + "type": "object" + }, + "privateOrganizations": { + "description": "Pattern searches for listed private organizations are disabled", + "type": "array" + }, + "urlShortener": { + "description": "Customisable URL shortener to share in proxy responses and warnings", + "type": "string" + }, + "contactEmail": { + "description": "Customisable e-mail address to share in proxy responses and warnings", + "type": "string" + }, + "csrfProtection": { + "description": "Flag to enable CSRF protections for UI", + "type": "boolean" + }, "authorisedList": { "description": "List of repositories that are authorised to be pushed to through the proxy.", "type": "array", @@ -46,7 +77,7 @@ "name": { "type": "string" }, "url": { "type": "string" } }, - "required": [ "project", "name", "url" ] + "required": ["project", "name", "url"] }, "database": { "type": "object", @@ -57,7 +88,7 @@ "options": { "type": "object" }, "params": { "type": "object" } }, - "required": [ "type", "enabled" ] + "required": ["type", "enabled"] }, "authentication": { "type": "object", @@ -66,8 +97,8 @@ "enabled": { "type": "boolean" }, "options": { "type": "object" } }, - "required": [ "type", "enabled" ] + "required": ["type", "enabled"] } }, "additionalProperties": false -} \ No newline at end of file +} diff --git a/index.html b/index.html index 8d8c5f33..ebdfd589 100644 --- a/index.html +++ b/index.html @@ -18,14 +18,11 @@ */ --> - + - + - - + + - + + + - - @@ -73,7 +68,6 @@
- - \ No newline at end of file + + diff --git a/package-lock.json b/package-lock.json index 83a31bcd..c33d74f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,22 +14,22 @@ "dependencies": { "@material-ui/core": "^4.11.0", "@material-ui/icons": "4.11.3", + "@primer/octicons-react": "^19.8.0", "@seald-io/nedb": "^4.0.2", "axios": "^1.6.0", + "bcrypt": "^5.1.1", "bit-mask": "^1.0.2", "body-parser": "^1.20.1", "chai-http": "^4.3.0", - "chartist": "0.10.1", "classnames": "2.5.1", "concurrently": "^8.0.0", + "connect-mongo": "^5.1.0", "cors": "^2.8.5", "diff2html": "^3.4.33", - "email-validator": "^2.0.4", "express": "^4.18.2", "express-http-proxy": "^2.0.0", "express-rate-limit": "^7.1.5", "express-session": "^1.17.1", - "generate-password": "^1.5.1", "history": "5.3.0", "jsonschema": "^1.4.1", "load-plugin": "^6.0.0", @@ -41,10 +41,9 @@ "passport": "^0.7.0", "passport-activedirectory": "^1.0.4", "passport-local": "^1.0.0", - "password-hash": "^1.2.2", + "perfect-scrollbar": "^1.5.5", "prop-types": "15.8.1", "react": "^16.13.1", - "react-chartist": "0.14.4", "react-dom": "^16.13.1", "react-html-parser": "^2.0.2", "react-router-dom": "6.23.0", @@ -52,23 +51,22 @@ "yargs": "^17.7.2" }, "bin": { - "git-proxy": "index.js" + "git-proxy": "index.js", + "git-proxy-all": "concurrently 'npm run server' 'npm run client'" }, "devDependencies": { + "@babel/core": "^7.23.2", "@babel/eslint-parser": "^7.22.9", "@babel/preset-react": "^7.22.5", "@commitlint/cli": "^19.0.0", "@commitlint/config-conventional": "^19.0.0", "@vitejs/plugin-react": "^4.0.2", "chai": "^4.2.0", - "eslint": "^8.0.0", + "eslint": "^8.57.0", "eslint-config-google": "^0.14.0", "eslint-config-prettier": "^9.0.0", - "eslint-config-standard": "^17.0.0", - "eslint-plugin-import": "^2.22.1", - "eslint-plugin-node": "^11.1.0", + "eslint-plugin-json": "^3.1.0", "eslint-plugin-prettier": "^5.0.0", - "eslint-plugin-promise": "^6.0.0", "eslint-plugin-react": "^7.21.5", "eslint-plugin-standard": "^5.0.0", "husky": "^9.0.0", @@ -100,1219 +98,1598 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", - "dev": true, + "node_modules/@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "optional": true, + "peer": true, "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/crc32/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true, + "peer": true + }, + "node_modules/@aws-crypto/ie11-detection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true, + "peer": true + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "optional": true, + "peer": true, + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true, + "peer": true + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "optional": true, + "peer": true, + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true, + "peer": true + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true, + "peer": true + }, + "node_modules/@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "optional": true, + "peer": true, + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true, + "peer": true + }, + "node_modules/@aws-sdk/client-cognito-identity": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.535.0.tgz", + "integrity": "sha512-7n9WAXAQzDgdaNkZlgdX+dmCW30tCrq3NpPs/f1WFWcF6g+s06ULkuWywTU+usG6ZTuRtajFKy2oMkMv9Wor0g==", + "optional": true, + "peer": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.535.0", + "@aws-sdk/core": "3.535.0", + "@aws-sdk/credential-provider-node": "3.535.0", + "@aws-sdk/middleware-host-header": "3.535.0", + "@aws-sdk/middleware-logger": "3.535.0", + "@aws-sdk/middleware-recursion-detection": "3.535.0", + "@aws-sdk/middleware-user-agent": "3.535.0", + "@aws-sdk/region-config-resolver": "3.535.0", + "@aws-sdk/types": "3.535.0", + "@aws-sdk/util-endpoints": "3.535.0", + "@aws-sdk/util-user-agent-browser": "3.535.0", + "@aws-sdk/util-user-agent-node": "3.535.0", + "@smithy/config-resolver": "^2.2.0", + "@smithy/core": "^1.4.0", + "@smithy/fetch-http-handler": "^2.5.0", + "@smithy/hash-node": "^2.2.0", + "@smithy/invalid-dependency": "^2.2.0", + "@smithy/middleware-content-length": "^2.2.0", + "@smithy/middleware-endpoint": "^2.5.0", + "@smithy/middleware-retry": "^2.2.0", + "@smithy/middleware-serde": "^2.3.0", + "@smithy/middleware-stack": "^2.2.0", + "@smithy/node-config-provider": "^2.3.0", + "@smithy/node-http-handler": "^2.5.0", + "@smithy/protocol-http": "^3.3.0", + "@smithy/smithy-client": "^2.5.0", + "@smithy/types": "^2.12.0", + "@smithy/url-parser": "^2.2.0", + "@smithy/util-base64": "^2.3.0", + "@smithy/util-body-length-browser": "^2.2.0", + "@smithy/util-body-length-node": "^2.3.0", + "@smithy/util-defaults-mode-browser": "^2.2.0", + "@smithy/util-defaults-mode-node": "^2.3.0", + "@smithy/util-endpoints": "^1.2.0", + "@smithy/util-middleware": "^2.2.0", + "@smithy/util-retry": "^2.2.0", + "@smithy/util-utf8": "^2.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/compat-data": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", - "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", - "dev": true, + "node_modules/@aws-sdk/client-sso": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.535.0.tgz", + "integrity": "sha512-h9eQRdFnjDRVBnPJIKXuX7D+isSAioIfZPC4PQwsL5BscTRlk4c90DX0R0uk64YUtp7LZu8TNtrosFZ/1HtTrQ==", + "optional": true, + "peer": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.535.0", + "@aws-sdk/middleware-host-header": "3.535.0", + "@aws-sdk/middleware-logger": "3.535.0", + "@aws-sdk/middleware-recursion-detection": "3.535.0", + "@aws-sdk/middleware-user-agent": "3.535.0", + "@aws-sdk/region-config-resolver": "3.535.0", + "@aws-sdk/types": "3.535.0", + "@aws-sdk/util-endpoints": "3.535.0", + "@aws-sdk/util-user-agent-browser": "3.535.0", + "@aws-sdk/util-user-agent-node": "3.535.0", + "@smithy/config-resolver": "^2.2.0", + "@smithy/core": "^1.4.0", + "@smithy/fetch-http-handler": "^2.5.0", + "@smithy/hash-node": "^2.2.0", + "@smithy/invalid-dependency": "^2.2.0", + "@smithy/middleware-content-length": "^2.2.0", + "@smithy/middleware-endpoint": "^2.5.0", + "@smithy/middleware-retry": "^2.2.0", + "@smithy/middleware-serde": "^2.3.0", + "@smithy/middleware-stack": "^2.2.0", + "@smithy/node-config-provider": "^2.3.0", + "@smithy/node-http-handler": "^2.5.0", + "@smithy/protocol-http": "^3.3.0", + "@smithy/smithy-client": "^2.5.0", + "@smithy/types": "^2.12.0", + "@smithy/url-parser": "^2.2.0", + "@smithy/util-base64": "^2.3.0", + "@smithy/util-body-length-browser": "^2.2.0", + "@smithy/util-body-length-node": "^2.3.0", + "@smithy/util-defaults-mode-browser": "^2.2.0", + "@smithy/util-defaults-mode-node": "^2.3.0", + "@smithy/util-endpoints": "^1.2.0", + "@smithy/util-middleware": "^2.2.0", + "@smithy/util-retry": "^2.2.0", + "@smithy/util-utf8": "^2.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/core": { - "version": "7.23.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz", - "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==", - "dev": true, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.535.0.tgz", + "integrity": "sha512-M2cG4EQXDpAJQyq33ORIr6abmdX9p9zX0ssVy8XwFNB7lrgoIKxuVoGL+fX+XMgecl24x7ELz6b4QlILOevbCw==", + "optional": true, + "peer": true, "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.7", - "@babel/parser": "^7.23.6", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.7", - "@babel/types": "^7.23.6", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.535.0", + "@aws-sdk/core": "3.535.0", + "@aws-sdk/middleware-host-header": "3.535.0", + "@aws-sdk/middleware-logger": "3.535.0", + "@aws-sdk/middleware-recursion-detection": "3.535.0", + "@aws-sdk/middleware-user-agent": "3.535.0", + "@aws-sdk/region-config-resolver": "3.535.0", + "@aws-sdk/types": "3.535.0", + "@aws-sdk/util-endpoints": "3.535.0", + "@aws-sdk/util-user-agent-browser": "3.535.0", + "@aws-sdk/util-user-agent-node": "3.535.0", + "@smithy/config-resolver": "^2.2.0", + "@smithy/core": "^1.4.0", + "@smithy/fetch-http-handler": "^2.5.0", + "@smithy/hash-node": "^2.2.0", + "@smithy/invalid-dependency": "^2.2.0", + "@smithy/middleware-content-length": "^2.2.0", + "@smithy/middleware-endpoint": "^2.5.0", + "@smithy/middleware-retry": "^2.2.0", + "@smithy/middleware-serde": "^2.3.0", + "@smithy/middleware-stack": "^2.2.0", + "@smithy/node-config-provider": "^2.3.0", + "@smithy/node-http-handler": "^2.5.0", + "@smithy/protocol-http": "^3.3.0", + "@smithy/smithy-client": "^2.5.0", + "@smithy/types": "^2.12.0", + "@smithy/url-parser": "^2.2.0", + "@smithy/util-base64": "^2.3.0", + "@smithy/util-body-length-browser": "^2.2.0", + "@smithy/util-body-length-node": "^2.3.0", + "@smithy/util-defaults-mode-browser": "^2.2.0", + "@smithy/util-defaults-mode-node": "^2.3.0", + "@smithy/util-endpoints": "^1.2.0", + "@smithy/util-middleware": "^2.2.0", + "@smithy/util-retry": "^2.2.0", + "@smithy/util-utf8": "^2.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "peerDependencies": { + "@aws-sdk/credential-provider-node": "^3.535.0" } }, - "node_modules/@babel/eslint-parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.24.1.tgz", - "integrity": "sha512-d5guuzMlPeDfZIbpQ8+g1NaCNuAGBBGNECh0HVqz1sjOeVLh2CEaifuOysCH18URW6R7pqXINvf5PaR/dC6jLQ==", - "dev": true, + "node_modules/@aws-sdk/client-sts": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.535.0.tgz", + "integrity": "sha512-ii9OOm3TJwP3JmO1IVJXKWIShVKPl0VtdlgROc/SkDglO/kuAw9eDdlROgc+qbFl+gm6bBTguOVTUXt3tS3flw==", + "optional": true, + "peer": true, "dependencies": { - "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", - "eslint-visitor-keys": "^2.1.0", - "semver": "^6.3.1" + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.535.0", + "@aws-sdk/middleware-host-header": "3.535.0", + "@aws-sdk/middleware-logger": "3.535.0", + "@aws-sdk/middleware-recursion-detection": "3.535.0", + "@aws-sdk/middleware-user-agent": "3.535.0", + "@aws-sdk/region-config-resolver": "3.535.0", + "@aws-sdk/types": "3.535.0", + "@aws-sdk/util-endpoints": "3.535.0", + "@aws-sdk/util-user-agent-browser": "3.535.0", + "@aws-sdk/util-user-agent-node": "3.535.0", + "@smithy/config-resolver": "^2.2.0", + "@smithy/core": "^1.4.0", + "@smithy/fetch-http-handler": "^2.5.0", + "@smithy/hash-node": "^2.2.0", + "@smithy/invalid-dependency": "^2.2.0", + "@smithy/middleware-content-length": "^2.2.0", + "@smithy/middleware-endpoint": "^2.5.0", + "@smithy/middleware-retry": "^2.2.0", + "@smithy/middleware-serde": "^2.3.0", + "@smithy/middleware-stack": "^2.2.0", + "@smithy/node-config-provider": "^2.3.0", + "@smithy/node-http-handler": "^2.5.0", + "@smithy/protocol-http": "^3.3.0", + "@smithy/smithy-client": "^2.5.0", + "@smithy/types": "^2.12.0", + "@smithy/url-parser": "^2.2.0", + "@smithy/util-base64": "^2.3.0", + "@smithy/util-body-length-browser": "^2.2.0", + "@smithy/util-body-length-node": "^2.3.0", + "@smithy/util-defaults-mode-browser": "^2.2.0", + "@smithy/util-defaults-mode-node": "^2.3.0", + "@smithy/util-endpoints": "^1.2.0", + "@smithy/util-middleware": "^2.2.0", + "@smithy/util-retry": "^2.2.0", + "@smithy/util-utf8": "^2.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || >=14.0.0" + "node": ">=14.0.0" }, "peerDependencies": { - "@babel/core": "^7.11.0", - "eslint": "^7.5.0 || ^8.0.0" + "@aws-sdk/credential-provider-node": "^3.535.0" } }, - "node_modules/@babel/generator": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", - "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", - "dev": true, + "node_modules/@aws-sdk/core": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.535.0.tgz", + "integrity": "sha512-+Yusa9HziuaEDta1UaLEtMAtmgvxdxhPn7jgfRY6PplqAqgsfa5FR83sxy5qr2q7xjQTwHtV4MjQVuOjG9JsLw==", + "optional": true, + "peer": true, "dependencies": { - "@babel/types": "^7.23.6", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" + "@smithy/core": "^1.4.0", + "@smithy/protocol-http": "^3.3.0", + "@smithy/signature-v4": "^2.2.0", + "@smithy/smithy-client": "^2.5.0", + "@smithy/types": "^2.12.0", + "fast-xml-parser": "4.2.5", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-cognito-identity": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.535.0.tgz", + "integrity": "sha512-Lc+RJTNzp22H31W/O7iSmCZUP+KYZMuzK8hKU4/RXo7D8t/cFLb4VpvvcCCa4UOZqdmxVqEhwc1oXyMkoszITQ==", + "optional": true, + "peer": true, "dependencies": { - "@babel/types": "^7.22.5" + "@aws-sdk/client-cognito-identity": "3.535.0", + "@aws-sdk/types": "3.535.0", + "@smithy/property-provider": "^2.2.0", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.535.0.tgz", + "integrity": "sha512-XppwO8c0GCGSAvdzyJOhbtktSEaShg14VJKg8mpMa1XcgqzmcqqHQjtDWbx5rZheY1VdpXZhpEzJkB6LpQejpA==", + "optional": true, + "peer": true, "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" + "@aws-sdk/types": "3.535.0", + "@smithy/property-provider": "^2.2.0", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.535.0.tgz", + "integrity": "sha512-kdj1wCmOMZ29jSlUskRqN04S6fJ4dvt0Nq9Z32SA6wO7UG8ht6Ot9h/au/eTWJM3E1somZ7D771oK7dQt9b8yw==", + "optional": true, + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.535.0", + "@smithy/fetch-http-handler": "^2.5.0", + "@smithy/node-http-handler": "^2.5.0", + "@smithy/property-provider": "^2.2.0", + "@smithy/protocol-http": "^3.3.0", + "@smithy/smithy-client": "^2.5.0", + "@smithy/types": "^2.12.0", + "@smithy/util-stream": "^2.2.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.535.0.tgz", + "integrity": "sha512-bm3XOYlyCjtAb8eeHXLrxqRxYVRw2Iqv9IufdJb4gM13TbNSYniUT1WKaHxGIZ5p+FuNlXVhvk1OpHFM13+gXA==", + "optional": true, + "peer": true, "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" + "@aws-sdk/client-sts": "3.535.0", + "@aws-sdk/credential-provider-env": "3.535.0", + "@aws-sdk/credential-provider-process": "3.535.0", + "@aws-sdk/credential-provider-sso": "3.535.0", + "@aws-sdk/credential-provider-web-identity": "3.535.0", + "@aws-sdk/types": "3.535.0", + "@smithy/credential-provider-imds": "^2.3.0", + "@smithy/property-provider": "^2.2.0", + "@smithy/shared-ini-file-loader": "^2.4.0", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.535.0.tgz", + "integrity": "sha512-6JXp/EuL6euUkH5k4d+lQFF6gBwukrcCOWfNHCmq14mNJf/cqT3HAX1VMtWFRSK20am0IxfYQGccb0/nZykdKg==", + "optional": true, + "peer": true, "dependencies": { - "@babel/types": "^7.22.5" + "@aws-sdk/credential-provider-env": "3.535.0", + "@aws-sdk/credential-provider-http": "3.535.0", + "@aws-sdk/credential-provider-ini": "3.535.0", + "@aws-sdk/credential-provider-process": "3.535.0", + "@aws-sdk/credential-provider-sso": "3.535.0", + "@aws-sdk/credential-provider-web-identity": "3.535.0", + "@aws-sdk/types": "3.535.0", + "@smithy/credential-provider-imds": "^2.3.0", + "@smithy/property-provider": "^2.2.0", + "@smithy/shared-ini-file-loader": "^2.4.0", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.535.0.tgz", + "integrity": "sha512-9O1OaprGCnlb/kYl8RwmH7Mlg8JREZctB8r9sa1KhSsWFq/SWO0AuJTyowxD7zL5PkeS4eTvzFFHWCa3OO5epA==", + "optional": true, + "peer": true, "dependencies": { - "@babel/types": "^7.22.15" + "@aws-sdk/types": "3.535.0", + "@smithy/property-provider": "^2.2.0", + "@smithy/shared-ini-file-loader": "^2.4.0", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.535.0.tgz", + "integrity": "sha512-2Dw0YIr8ETdFpq65CC4zK8ZIEbX78rXoNRZXUGNQW3oSKfL0tj8O8ErY6kg1IdEnYbGnEQ35q6luZ5GGNKLgDg==", + "optional": true, + "peer": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" + "@aws-sdk/client-sso": "3.535.0", + "@aws-sdk/token-providers": "3.535.0", + "@aws-sdk/types": "3.535.0", + "@smithy/property-provider": "^2.2.0", + "@smithy/shared-ini-file-loader": "^2.4.0", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", - "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.535.0.tgz", + "integrity": "sha512-t2/JWrKY0H66A7JW7CqX06/DG2YkJddikt5ymdQvx/Q7dRMJ3d+o/vgjoKr7RvEx/pNruCeyM1599HCvwrVMrg==", + "optional": true, + "peer": true, + "dependencies": { + "@aws-sdk/client-sts": "3.535.0", + "@aws-sdk/types": "3.535.0", + "@smithy/property-provider": "^2.2.0", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, + "node_modules/@aws-sdk/credential-providers": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.535.0.tgz", + "integrity": "sha512-rC3TguTFbeua3EyTwGm84xeARKE1RO0oIWdtuTmSS5ZCPwllcePGkOVg7gQiPRc01Ebj816S/6P2QbvAfSUxqA==", + "optional": true, + "peer": true, "dependencies": { - "@babel/types": "^7.22.5" + "@aws-sdk/client-cognito-identity": "3.535.0", + "@aws-sdk/client-sso": "3.535.0", + "@aws-sdk/client-sts": "3.535.0", + "@aws-sdk/credential-provider-cognito-identity": "3.535.0", + "@aws-sdk/credential-provider-env": "3.535.0", + "@aws-sdk/credential-provider-http": "3.535.0", + "@aws-sdk/credential-provider-ini": "3.535.0", + "@aws-sdk/credential-provider-node": "3.535.0", + "@aws-sdk/credential-provider-process": "3.535.0", + "@aws-sdk/credential-provider-sso": "3.535.0", + "@aws-sdk/credential-provider-web-identity": "3.535.0", + "@aws-sdk/types": "3.535.0", + "@smithy/credential-provider-imds": "^2.3.0", + "@smithy/property-provider": "^2.2.0", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.535.0.tgz", + "integrity": "sha512-0h6TWjBWtDaYwHMQJI9ulafeS4lLaw1vIxRjbpH0svFRt6Eve+Sy8NlVhECfTU2hNz/fLubvrUxsXoThaLBIew==", + "optional": true, + "peer": true, "dependencies": { - "@babel/types": "^7.22.5" + "@aws-sdk/types": "3.535.0", + "@smithy/protocol-http": "^3.3.0", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", - "dev": true, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.535.0.tgz", + "integrity": "sha512-huNHpONOrEDrdRTvSQr1cJiRMNf0S52NDXtaPzdxiubTkP+vni2MohmZANMOai/qT0olmEVX01LhZ0ZAOgmg6A==", + "optional": true, + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.535.0", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.535.0.tgz", + "integrity": "sha512-am2qgGs+gwqmR4wHLWpzlZ8PWhm4ktj5bYSgDrsOfjhdBlWNxvPoID9/pDAz5RWL48+oH7I6SQzMqxXsFDikrw==", + "optional": true, + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.535.0", + "@smithy/protocol-http": "^3.3.0", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", - "dev": true, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.535.0.tgz", + "integrity": "sha512-Uvb2WJ+zdHdCOtsWVPI/M0BcfNrjOYsicDZWtaljucRJKLclY5gNWwD+RwIC+8b5TvfnVOlH+N5jhvpi5Impog==", + "optional": true, + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.535.0", + "@aws-sdk/util-endpoints": "3.535.0", + "@smithy/protocol-http": "^3.3.0", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helpers": { - "version": "7.23.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.7.tgz", - "integrity": "sha512-6AMnjCoC8wjqBzDHkuqpa7jAKwvMo4dC+lr/TFBz+ucfulO1XMpDnwWPGBNwClOKZ8h6xn5N81W/R5OrcKtCbQ==", - "dev": true, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.535.0.tgz", + "integrity": "sha512-IXOznDiaItBjsQy4Fil0kzX/J3HxIOknEphqHbOfUf+LpA5ugcsxuQQONrbEQusCBnfJyymrldBvBhFmtlU9Wg==", + "optional": true, + "peer": true, "dependencies": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.7", - "@babel/types": "^7.23.6" + "@aws-sdk/types": "3.535.0", + "@smithy/node-config-provider": "^2.3.0", + "@smithy/types": "^2.12.0", + "@smithy/util-config-provider": "^2.3.0", + "@smithy/util-middleware": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", - "dev": true, + "node_modules/@aws-sdk/token-providers": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.535.0.tgz", + "integrity": "sha512-4g+l/B9h1H/SiDtFRosW3pMwc+3PTXljZit+5NUBcET2XqcdUyHmgj3lBdu+CJ9CHdIMggRalYMAFXnRFe3Psg==", + "optional": true, + "peer": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "@aws-sdk/client-sso-oidc": "3.535.0", + "@aws-sdk/types": "3.535.0", + "@smithy/property-provider": "^2.2.0", + "@smithy/shared-ini-file-loader": "^2.4.0", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/parser": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", - "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" + "node_modules/@aws-sdk/types": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.535.0.tgz", + "integrity": "sha512-aY4MYfduNj+sRR37U7XxYR8wemfbKP6lx00ze2M2uubn7mZotuVrWYAafbMSXrdEMSToE5JDhr28vArSOoLcSg==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.0.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", - "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", - "dev": true, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.535.0.tgz", + "integrity": "sha512-c8TlaQsiPchOOmTTR6qvHCO2O7L7NJwlKWAoQJ2GqWDZuC5es/fyuF2rp1h+ZRrUVraUomS0YdGkAmaDC7hJQg==", + "optional": true, + "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@aws-sdk/types": "3.535.0", + "@smithy/types": "^2.12.0", + "@smithy/util-endpoints": "^1.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=14.0.0" } }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.1.tgz", - "integrity": "sha512-mvoQg2f9p2qlpDQRBC7M3c3XTr0k7cp/0+kFKKO/7Gtu0LSw16eKB+Fabe2bDT/UpsyasTBBkAnbdsLrkD5XMw==", - "dev": true, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.535.0.tgz", + "integrity": "sha512-PHJ3SL6d2jpcgbqdgiPxkXpu7Drc2PYViwxSIqvvMKhDwzSB1W3mMvtpzwKM4IE7zLFodZo0GKjJ9AsoXndXhA==", + "optional": true, + "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=14.0.0" } }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", - "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==", - "dev": true, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.535.0.tgz", + "integrity": "sha512-RWMcF/xV5n+nhaA/Ff5P3yNP3Kur/I+VNZngog4TEs92oB/nwOdAg/2JL8bVAhUbMrjTjpwm7PItziYFQoqyig==", + "optional": true, + "peer": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.23.3", - "@babel/types": "^7.23.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@aws-sdk/types": "3.535.0", + "@smithy/types": "^2.12.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" } }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", - "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", - "dev": true, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.535.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.535.0.tgz", + "integrity": "sha512-dRek0zUuIT25wOWJlsRm97nTkUlh1NDcLsQZIN2Y8KxhwoXXWtJs5vaDPT+qAg+OpcNj80i1zLR/CirqlFg/TQ==", + "optional": true, + "peer": true, "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.22.5" + "@aws-sdk/types": "3.535.0", + "@smithy/node-config-provider": "^2.3.0", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz", - "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==", + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz", - "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==", + "node_modules/@babel/compat-data": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.1.tgz", - "integrity": "sha512-+pWEAaDJvSm9aFvJNpLiM2+ktl2Sn2U5DdyiWdZBxmLc6+xGt88dvFqsHiAiDS+8WqUwbDfkKz9jRxK3M0k+kA==", + "node_modules/@babel/core": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz", + "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-plugin-utils": "^7.24.0" + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.7", + "@babel/parser": "^7.23.6", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/preset-react": { + "node_modules/@babel/eslint-parser": { "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.1.tgz", - "integrity": "sha512-eFa8up2/8cZXLIpkafhaADTXSnl7IsUFCYenRWrARBz0/qZwcT0RBXpys0LJU4+WfPoF2ZG6ew6s2V6izMCwRA==", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.24.1.tgz", + "integrity": "sha512-d5guuzMlPeDfZIbpQ8+g1NaCNuAGBBGNECh0HVqz1sjOeVLh2CEaifuOysCH18URW6R7pqXINvf5PaR/dC6jLQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-transform-react-display-name": "^7.24.1", - "@babel/plugin-transform-react-jsx": "^7.23.4", - "@babel/plugin-transform-react-jsx-development": "^7.22.5", - "@babel/plugin-transform-react-pure-annotations": "^7.24.1" + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.1" }, "engines": { - "node": ">=6.9.0" + "node": "^10.13.0 || ^12.13.0 || >=14.0.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.11.0", + "eslint": "^7.5.0 || ^8.0.0" } }, - "node_modules/@babel/runtime": { - "version": "7.23.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.7.tgz", - "integrity": "sha512-w06OXVOFso7LcbzMiDGt+3X7Rh7Ho8MmgPoWU3rarH+8upf+wSU/grlGbWzQyr3DkdN6ZeuMFjpdwW0Q+HxobA==", + "node_modules/@babel/generator": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "dev": true, "dependencies": { - "regenerator-runtime": "^0.14.0" + "@babel/types": "^7.23.6", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/traverse": { - "version": "7.23.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz", - "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==", - "dev": true, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dev": true, "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.6", - "@babel/types": "^7.23.6", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/types": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", - "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@commitlint/cli": { - "version": "19.3.0", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.3.0.tgz", - "integrity": "sha512-LgYWOwuDR7BSTQ9OLZ12m7F/qhNY+NpAyPBgo4YNMkACE7lGuUnuQq1yi9hz1KA4+3VqpOYl8H1rY/LYK43v7g==", + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@commitlint/format": "^19.3.0", - "@commitlint/lint": "^19.2.2", - "@commitlint/load": "^19.2.0", - "@commitlint/read": "^19.2.1", - "@commitlint/types": "^19.0.3", - "execa": "^8.0.1", - "yargs": "^17.0.0" - }, - "bin": { - "commitlint": "cli.js" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { - "node": ">=v18" + "node": ">=6.9.0" } }, - "node_modules/@commitlint/config-conventional": { - "version": "19.2.2", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-19.2.2.tgz", - "integrity": "sha512-mLXjsxUVLYEGgzbxbxicGPggDuyWNkf25Ht23owXIH+zV2pv1eJuzLK3t1gDY5Gp6pxdE60jZnWUY5cvgL3ufw==", + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@commitlint/types": "^19.0.3", - "conventional-changelog-conventionalcommits": "^7.0.2" + "@babel/types": "^7.22.5" }, "engines": { - "node": ">=v18" + "node": ">=6.9.0" } }, - "node_modules/@commitlint/config-validator": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.0.3.tgz", - "integrity": "sha512-2D3r4PKjoo59zBc2auodrSCaUnCSALCx54yveOFwwP/i2kfEAQrygwOleFWswLqK0UL/F9r07MFi5ev2ohyM4Q==", + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dev": true, "dependencies": { - "@commitlint/types": "^19.0.3", - "ajv": "^8.11.0" + "@babel/types": "^7.22.15" }, "engines": { - "node": ">=v18" + "node": ">=6.9.0" } }, - "node_modules/@commitlint/ensure": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-19.0.3.tgz", - "integrity": "sha512-SZEpa/VvBLoT+EFZVb91YWbmaZ/9rPH3ESrINOl0HD2kMYsjvl0tF7nMHh0EpTcv4+gTtZBAe1y/SS6/OhfZzQ==", + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dev": true, "dependencies": { - "@commitlint/types": "^19.0.3", - "lodash.camelcase": "^4.3.0", - "lodash.kebabcase": "^4.1.1", - "lodash.snakecase": "^4.1.1", - "lodash.startcase": "^4.4.0", - "lodash.upperfirst": "^4.3.1" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { - "node": ">=v18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@commitlint/execute-rule": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.0.0.tgz", - "integrity": "sha512-mtsdpY1qyWgAO/iOK0L6gSGeR7GFcdW7tIjcNFxcWkfLDF5qVbPHKuGATFqRMsxcO8OUKNj0+3WOHB7EHm4Jdw==", + "node_modules/@babel/helper-plugin-utils": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", + "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", "dev": true, "engines": { - "node": ">=v18" + "node": ">=6.9.0" } }, - "node_modules/@commitlint/format": { - "version": "19.3.0", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.3.0.tgz", - "integrity": "sha512-luguk5/aF68HiF4H23ACAfk8qS8AHxl4LLN5oxPc24H+2+JRPsNr1OS3Gaea0CrH7PKhArBMKBz5RX9sA5NtTg==", + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "dependencies": { - "@commitlint/types": "^19.0.3", - "chalk": "^5.3.0" + "@babel/types": "^7.22.5" }, "engines": { - "node": ">=v18" + "node": ">=6.9.0" } }, - "node_modules/@commitlint/format/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" + "dependencies": { + "@babel/types": "^7.22.5" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@commitlint/is-ignored": { - "version": "19.2.2", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-19.2.2.tgz", - "integrity": "sha512-eNX54oXMVxncORywF4ZPFtJoBm3Tvp111tg1xf4zWXGfhBPKpfKG6R+G3G4v5CPlRROXpAOpQ3HMhA9n1Tck1g==", + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "dev": true, - "dependencies": { - "@commitlint/types": "^19.0.3", - "semver": "^7.6.0" - }, "engines": { - "node": ">=v18" + "node": ">=6.9.0" } }, - "node_modules/@commitlint/is-ignored/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": ">=6.9.0" } }, - "node_modules/@commitlint/is-ignored/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, "engines": { - "node": ">=10" + "node": ">=6.9.0" } }, - "node_modules/@commitlint/is-ignored/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/@commitlint/lint": { - "version": "19.2.2", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.2.2.tgz", - "integrity": "sha512-xrzMmz4JqwGyKQKTpFzlN0dx0TAiT7Ran1fqEBgEmEj+PU98crOFtysJgY+QdeSagx6EDRigQIXJVnfrI0ratA==", + "node_modules/@babel/helpers": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.7.tgz", + "integrity": "sha512-6AMnjCoC8wjqBzDHkuqpa7jAKwvMo4dC+lr/TFBz+ucfulO1XMpDnwWPGBNwClOKZ8h6xn5N81W/R5OrcKtCbQ==", "dev": true, "dependencies": { - "@commitlint/is-ignored": "^19.2.2", - "@commitlint/parse": "^19.0.3", - "@commitlint/rules": "^19.0.3", - "@commitlint/types": "^19.0.3" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6" }, "engines": { - "node": ">=v18" + "node": ">=6.9.0" } }, - "node_modules/@commitlint/load": { - "version": "19.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.2.0.tgz", - "integrity": "sha512-XvxxLJTKqZojCxaBQ7u92qQLFMMZc4+p9qrIq/9kJDy8DOrEa7P1yx7Tjdc2u2JxIalqT4KOGraVgCE7eCYJyQ==", + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, "dependencies": { - "@commitlint/config-validator": "^19.0.3", - "@commitlint/execute-rule": "^19.0.0", - "@commitlint/resolve-extends": "^19.1.0", - "@commitlint/types": "^19.0.3", - "chalk": "^5.3.0", - "cosmiconfig": "^9.0.0", - "cosmiconfig-typescript-loader": "^5.0.0", - "lodash.isplainobject": "^4.0.6", - "lodash.merge": "^4.6.2", - "lodash.uniq": "^4.5.0" + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" }, "engines": { - "node": ">=v18" + "node": ">=6.9.0" } }, - "node_modules/@commitlint/load/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "node_modules/@babel/parser": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" + "bin": { + "parser": "bin/babel-parser.js" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@commitlint/message": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-19.0.0.tgz", - "integrity": "sha512-c9czf6lU+9oF9gVVa2lmKaOARJvt4soRsVmbR7Njwp9FpbBgste5i7l/2l5o8MmbwGh4yE1snfnsy2qyA2r/Fw==", - "dev": true, "engines": { - "node": ">=v18" + "node": ">=6.0.0" } }, - "node_modules/@commitlint/parse": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-19.0.3.tgz", - "integrity": "sha512-Il+tNyOb8VDxN3P6XoBBwWJtKKGzHlitEuXA5BP6ir/3loWlsSqDr5aecl6hZcC/spjq4pHqNh0qPlfeWu38QA==", + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", "dev": true, "dependencies": { - "@commitlint/types": "^19.0.3", - "conventional-changelog-angular": "^7.0.0", - "conventional-commits-parser": "^5.0.0" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { - "node": ">=v18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@commitlint/read": { - "version": "19.2.1", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-19.2.1.tgz", - "integrity": "sha512-qETc4+PL0EUv7Q36lJbPG+NJiBOGg7SSC7B5BsPWOmei+Dyif80ErfWQ0qXoW9oCh7GTpTNRoaVhiI8RbhuaNw==", + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.1.tgz", + "integrity": "sha512-mvoQg2f9p2qlpDQRBC7M3c3XTr0k7cp/0+kFKKO/7Gtu0LSw16eKB+Fabe2bDT/UpsyasTBBkAnbdsLrkD5XMw==", "dev": true, "dependencies": { - "@commitlint/top-level": "^19.0.0", - "@commitlint/types": "^19.0.3", - "execa": "^8.0.1", - "git-raw-commits": "^4.0.0", - "minimist": "^1.2.8" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { - "node": ">=v18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@commitlint/resolve-extends": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.1.0.tgz", - "integrity": "sha512-z2riI+8G3CET5CPgXJPlzftH+RiWYLMYv4C9tSLdLXdr6pBNimSKukYP9MS27ejmscqCTVA4almdLh0ODD2KYg==", + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", + "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==", "dev": true, "dependencies": { - "@commitlint/config-validator": "^19.0.3", - "@commitlint/types": "^19.0.3", - "global-directory": "^4.0.1", - "import-meta-resolve": "^4.0.0", - "lodash.mergewith": "^4.6.2", - "resolve-from": "^5.0.0" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/types": "^7.23.4" }, "engines": { - "node": ">=v18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@commitlint/rules": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.0.3.tgz", - "integrity": "sha512-TspKb9VB6svklxNCKKwxhELn7qhtY1rFF8ls58DcFd0F97XoG07xugPjjbVnLqmMkRjZDbDIwBKt9bddOfLaPw==", + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", + "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", "dev": true, "dependencies": { - "@commitlint/ensure": "^19.0.3", - "@commitlint/message": "^19.0.0", - "@commitlint/to-lines": "^19.0.0", - "@commitlint/types": "^19.0.3", - "execa": "^8.0.1" + "@babel/plugin-transform-react-jsx": "^7.22.5" }, "engines": { - "node": ">=v18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@commitlint/to-lines": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-19.0.0.tgz", - "integrity": "sha512-vkxWo+VQU5wFhiP9Ub9Sre0FYe019JxFikrALVoD5UGa8/t3yOJEpEhxC5xKiENKKhUkTpEItMTRAjHw2SCpZw==", + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz", + "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==", "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, "engines": { - "node": ">=v18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@commitlint/top-level": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-19.0.0.tgz", - "integrity": "sha512-KKjShd6u1aMGNkCkaX4aG1jOGdn7f8ZI8TR1VEuNqUOjWTOdcDSsmglinglJ18JTjuBX5I1PtjrhQCRcixRVFQ==", + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz", + "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==", "dev": true, "dependencies": { - "find-up": "^7.0.0" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { - "node": ">=v18" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@commitlint/top-level/node_modules/find-up": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", - "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.1.tgz", + "integrity": "sha512-+pWEAaDJvSm9aFvJNpLiM2+ktl2Sn2U5DdyiWdZBxmLc6+xGt88dvFqsHiAiDS+8WqUwbDfkKz9jRxK3M0k+kA==", "dev": true, "dependencies": { - "locate-path": "^7.2.0", - "path-exists": "^5.0.0", - "unicorn-magic": "^0.1.0" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { - "node": ">=18" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@commitlint/top-level/node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "node_modules/@babel/preset-react": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.1.tgz", + "integrity": "sha512-eFa8up2/8cZXLIpkafhaADTXSnl7IsUFCYenRWrARBz0/qZwcT0RBXpys0LJU4+WfPoF2ZG6ew6s2V6izMCwRA==", "dev": true, "dependencies": { - "p-locate": "^6.0.0" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-transform-react-display-name": "^7.24.1", + "@babel/plugin-transform-react-jsx": "^7.23.4", + "@babel/plugin-transform-react-jsx-development": "^7.22.5", + "@babel/plugin-transform-react-pure-annotations": "^7.24.1" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@commitlint/top-level/node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "dev": true, + "node_modules/@babel/runtime": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.7.tgz", + "integrity": "sha512-w06OXVOFso7LcbzMiDGt+3X7Rh7Ho8MmgPoWU3rarH+8upf+wSU/grlGbWzQyr3DkdN6ZeuMFjpdwW0Q+HxobA==", "dependencies": { - "yocto-queue": "^1.0.0" + "regenerator-runtime": "^0.14.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6.9.0" } }, - "node_modules/@commitlint/top-level/node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "p-limit": "^4.0.0" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6.9.0" } }, - "node_modules/@commitlint/top-level/node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "node_modules/@babel/traverse": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz", + "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==", "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=6.9.0" } }, - "node_modules/@commitlint/top-level/node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "node_modules/@babel/types": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", "dev": true, - "engines": { - "node": ">=12.20" + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@commitlint/types": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.0.3.tgz", - "integrity": "sha512-tpyc+7i6bPG9mvaBbtKUeghfyZSDgWquIDfMgqYtTbmZ9Y9VzEm2je9EYcQ0aoz5o7NvGS+rcDec93yO08MHYA==", + "node_modules/@commitlint/cli": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.3.0.tgz", + "integrity": "sha512-LgYWOwuDR7BSTQ9OLZ12m7F/qhNY+NpAyPBgo4YNMkACE7lGuUnuQq1yi9hz1KA4+3VqpOYl8H1rY/LYK43v7g==", "dev": true, "dependencies": { - "@types/conventional-commits-parser": "^5.0.0", - "chalk": "^5.3.0" + "@commitlint/format": "^19.3.0", + "@commitlint/lint": "^19.2.2", + "@commitlint/load": "^19.2.0", + "@commitlint/read": "^19.2.1", + "@commitlint/types": "^19.0.3", + "execa": "^8.0.1", + "yargs": "^17.0.0" + }, + "bin": { + "commitlint": "cli.js" }, "engines": { "node": ">=v18" } }, - "node_modules/@commitlint/types/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "node_modules/@commitlint/config-conventional": { + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-19.2.2.tgz", + "integrity": "sha512-mLXjsxUVLYEGgzbxbxicGPggDuyWNkf25Ht23owXIH+zV2pv1eJuzLK3t1gDY5Gp6pxdE60jZnWUY5cvgL3ufw==", "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" + "dependencies": { + "@commitlint/types": "^19.0.3", + "conventional-changelog-conventionalcommits": "^7.0.2" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "engines": { + "node": ">=v18" } }, - "node_modules/@emotion/hash": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", - "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" - }, - "node_modules/@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", - "cpu": [ - "arm" - ], + "node_modules/@commitlint/config-validator": { + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.0.3.tgz", + "integrity": "sha512-2D3r4PKjoo59zBc2auodrSCaUnCSALCx54yveOFwwP/i2kfEAQrygwOleFWswLqK0UL/F9r07MFi5ev2ohyM4Q==", "dev": true, - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "@commitlint/types": "^19.0.3", + "ajv": "^8.11.0" + }, "engines": { - "node": ">=12" + "node": ">=v18" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", - "cpu": [ - "arm64" - ], + "node_modules/@commitlint/ensure": { + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-19.0.3.tgz", + "integrity": "sha512-SZEpa/VvBLoT+EFZVb91YWbmaZ/9rPH3ESrINOl0HD2kMYsjvl0tF7nMHh0EpTcv4+gTtZBAe1y/SS6/OhfZzQ==", "dev": true, - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "@commitlint/types": "^19.0.3", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.upperfirst": "^4.3.1" + }, "engines": { - "node": ">=12" + "node": ">=v18" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", - "cpu": [ - "x64" - ], + "node_modules/@commitlint/execute-rule": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.0.0.tgz", + "integrity": "sha512-mtsdpY1qyWgAO/iOK0L6gSGeR7GFcdW7tIjcNFxcWkfLDF5qVbPHKuGATFqRMsxcO8OUKNj0+3WOHB7EHm4Jdw==", "dev": true, - "optional": true, - "os": [ - "android" - ], "engines": { - "node": ">=12" + "node": ">=v18" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", - "cpu": [ - "arm64" - ], + "node_modules/@commitlint/format": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.3.0.tgz", + "integrity": "sha512-luguk5/aF68HiF4H23ACAfk8qS8AHxl4LLN5oxPc24H+2+JRPsNr1OS3Gaea0CrH7PKhArBMKBz5RX9sA5NtTg==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "@commitlint/types": "^19.0.3", + "chalk": "^5.3.0" + }, "engines": { - "node": ">=12" + "node": ">=v18" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", - "cpu": [ - "x64" - ], + "node_modules/@commitlint/format/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": ">=12" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", - "cpu": [ - "arm64" - ], + "node_modules/@commitlint/is-ignored": { + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-19.2.2.tgz", + "integrity": "sha512-eNX54oXMVxncORywF4ZPFtJoBm3Tvp111tg1xf4zWXGfhBPKpfKG6R+G3G4v5CPlRROXpAOpQ3HMhA9n1Tck1g==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "@commitlint/types": "^19.0.3", + "semver": "^7.6.0" + }, "engines": { - "node": ">=12" + "node": ">=v18" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", - "cpu": [ - "x64" - ], + "node_modules/@commitlint/is-ignored/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "yallist": "^4.0.0" + }, "engines": { - "node": ">=12" + "node": ">=10" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", - "cpu": [ - "arm" - ], + "node_modules/@commitlint/is-ignored/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=12" + "node": ">=10" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", - "cpu": [ - "arm64" - ], + "node_modules/@commitlint/is-ignored/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@commitlint/lint": { + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.2.2.tgz", + "integrity": "sha512-xrzMmz4JqwGyKQKTpFzlN0dx0TAiT7Ran1fqEBgEmEj+PU98crOFtysJgY+QdeSagx6EDRigQIXJVnfrI0ratA==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@commitlint/is-ignored": "^19.2.2", + "@commitlint/parse": "^19.0.3", + "@commitlint/rules": "^19.0.3", + "@commitlint/types": "^19.0.3" + }, "engines": { - "node": ">=12" + "node": ">=v18" } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", - "cpu": [ - "ia32" - ], + "node_modules/@commitlint/load": { + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.2.0.tgz", + "integrity": "sha512-XvxxLJTKqZojCxaBQ7u92qQLFMMZc4+p9qrIq/9kJDy8DOrEa7P1yx7Tjdc2u2JxIalqT4KOGraVgCE7eCYJyQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@commitlint/config-validator": "^19.0.3", + "@commitlint/execute-rule": "^19.0.0", + "@commitlint/resolve-extends": "^19.1.0", + "@commitlint/types": "^19.0.3", + "chalk": "^5.3.0", + "cosmiconfig": "^9.0.0", + "cosmiconfig-typescript-loader": "^5.0.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "lodash.uniq": "^4.5.0" + }, "engines": { - "node": ">=12" + "node": ">=v18" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", - "cpu": [ - "loong64" - ], + "node_modules/@commitlint/load/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=12" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", - "cpu": [ - "mips64el" - ], + "node_modules/@commitlint/message": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-19.0.0.tgz", + "integrity": "sha512-c9czf6lU+9oF9gVVa2lmKaOARJvt4soRsVmbR7Njwp9FpbBgste5i7l/2l5o8MmbwGh4yE1snfnsy2qyA2r/Fw==", "dev": true, - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=12" + "node": ">=v18" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", - "cpu": [ - "ppc64" - ], + "node_modules/@commitlint/parse": { + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-19.0.3.tgz", + "integrity": "sha512-Il+tNyOb8VDxN3P6XoBBwWJtKKGzHlitEuXA5BP6ir/3loWlsSqDr5aecl6hZcC/spjq4pHqNh0qPlfeWu38QA==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@commitlint/types": "^19.0.3", + "conventional-changelog-angular": "^7.0.0", + "conventional-commits-parser": "^5.0.0" + }, "engines": { - "node": ">=12" + "node": ">=v18" } }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", - "cpu": [ - "riscv64" - ], + "node_modules/@commitlint/read": { + "version": "19.2.1", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-19.2.1.tgz", + "integrity": "sha512-qETc4+PL0EUv7Q36lJbPG+NJiBOGg7SSC7B5BsPWOmei+Dyif80ErfWQ0qXoW9oCh7GTpTNRoaVhiI8RbhuaNw==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@commitlint/top-level": "^19.0.0", + "@commitlint/types": "^19.0.3", + "execa": "^8.0.1", + "git-raw-commits": "^4.0.0", + "minimist": "^1.2.8" + }, "engines": { - "node": ">=12" + "node": ">=v18" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", - "cpu": [ - "s390x" - ], + "node_modules/@commitlint/resolve-extends": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.1.0.tgz", + "integrity": "sha512-z2riI+8G3CET5CPgXJPlzftH+RiWYLMYv4C9tSLdLXdr6pBNimSKukYP9MS27ejmscqCTVA4almdLh0ODD2KYg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@commitlint/config-validator": "^19.0.3", + "@commitlint/types": "^19.0.3", + "global-directory": "^4.0.1", + "import-meta-resolve": "^4.0.0", + "lodash.mergewith": "^4.6.2", + "resolve-from": "^5.0.0" + }, "engines": { - "node": ">=12" + "node": ">=v18" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", - "cpu": [ - "x64" - ], + "node_modules/@commitlint/rules": { + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.0.3.tgz", + "integrity": "sha512-TspKb9VB6svklxNCKKwxhELn7qhtY1rFF8ls58DcFd0F97XoG07xugPjjbVnLqmMkRjZDbDIwBKt9bddOfLaPw==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@commitlint/ensure": "^19.0.3", + "@commitlint/message": "^19.0.0", + "@commitlint/to-lines": "^19.0.0", + "@commitlint/types": "^19.0.3", + "execa": "^8.0.1" + }, "engines": { - "node": ">=12" + "node": ">=v18" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", - "cpu": [ - "x64" - ], + "node_modules/@commitlint/to-lines": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-19.0.0.tgz", + "integrity": "sha512-vkxWo+VQU5wFhiP9Ub9Sre0FYe019JxFikrALVoD5UGa8/t3yOJEpEhxC5xKiENKKhUkTpEItMTRAjHw2SCpZw==", "dev": true, - "optional": true, - "os": [ - "netbsd" - ], "engines": { - "node": ">=12" + "node": ">=v18" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", - "cpu": [ - "x64" - ], + "node_modules/@commitlint/top-level": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-19.0.0.tgz", + "integrity": "sha512-KKjShd6u1aMGNkCkaX4aG1jOGdn7f8ZI8TR1VEuNqUOjWTOdcDSsmglinglJ18JTjuBX5I1PtjrhQCRcixRVFQ==", "dev": true, - "optional": true, - "os": [ - "openbsd" - ], + "dependencies": { + "find-up": "^7.0.0" + }, "engines": { - "node": ">=12" + "node": ">=v18" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", - "cpu": [ - "x64" - ], + "node_modules/@commitlint/top-level/node_modules/find-up": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", + "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", "dev": true, - "optional": true, - "os": [ - "sunos" - ], + "dependencies": { + "locate-path": "^7.2.0", + "path-exists": "^5.0.0", + "unicorn-magic": "^0.1.0" + }, "engines": { - "node": ">=12" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", - "cpu": [ - "arm64" - ], + "node_modules/@commitlint/top-level/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", "dev": true, - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "p-locate": "^6.0.0" + }, "engines": { - "node": ">=12" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", - "cpu": [ - "ia32" - ], + "node_modules/@commitlint/top-level/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", "dev": true, - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "yocto-queue": "^1.0.0" + }, "engines": { - "node": ">=12" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@commitlint/top-level/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/types": { + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.0.3.tgz", + "integrity": "sha512-tpyc+7i6bPG9mvaBbtKUeghfyZSDgWquIDfMgqYtTbmZ9Y9VzEm2je9EYcQ0aoz5o7NvGS+rcDec93yO08MHYA==", + "dev": true, + "dependencies": { + "@types/conventional-commits-parser": "^5.0.0", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/types/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@esbuild/win32-x64": { + "node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + }, + "node_modules/@esbuild/darwin-x64": { "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", - "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ - "win32" + "darwin" ], "engines": { "node": ">=12" @@ -1663,6 +2040,69 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/@material-ui/core": { "version": "4.12.4", "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.4.tgz", @@ -1897,139 +2337,763 @@ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/config/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/config/node_modules/nopt": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz", + "integrity": "sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/config/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/config/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/@npmcli/map-workspaces": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-3.0.4.tgz", + "integrity": "sha512-Z0TbvXkRbacjFFLpVpV0e2mheCh+WzQpcqL+4xp49uNJOxOnIAPZyXtUxZ5Qn3QBTGKA11Exjd9a5411rBrhDg==", + "dependencies": { + "@npmcli/name-from-folder": "^2.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0", + "read-package-json-fast": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/name-from-folder": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-2.0.0.tgz", + "integrity": "sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg==", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.0.tgz", + "integrity": "sha512-Zwq5OCzuwJC2jwqmpEQt7Ds1DTi6BWSwoGkbb1n9pO3hzb35BoJELx7c0T23iDkBGkh2e7tvOtjF3tr3OaQHDQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@primer/octicons-react": { + "version": "19.9.0", + "resolved": "https://registry.npmjs.org/@primer/octicons-react/-/octicons-react-19.9.0.tgz", + "integrity": "sha512-Uk4XrHyfylyfzZN9d8VFjF8FpfYHEyT4sabw+9+oP+GWAJHhPvNPTz6gXvUzJZmoblAvgcTrDslIPjz8zMh76w==", + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.3" + } + }, + "node_modules/@remix-run/router": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.0.tgz", + "integrity": "sha512-Quz1KOffeEf/zwkCBM3kBtH4ZoZ+pT3xIXBG4PPW/XFtDP7EGhtTiC2+gpL9GnR7+Qdet5Oa6cYSvwKYg6kN9Q==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@seald-io/binary-search-tree": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@seald-io/binary-search-tree/-/binary-search-tree-1.0.3.tgz", + "integrity": "sha512-qv3jnwoakeax2razYaMsGI/luWdliBLHTdC6jU55hQt1hcFqzauH/HsBollQ7IR4ySTtYhT+xyHoijpA16C+tA==" + }, + "node_modules/@seald-io/nedb": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@seald-io/nedb/-/nedb-4.0.4.tgz", + "integrity": "sha512-CUNcMio7QUHTA+sIJ/DC5JzVNNsHe743TPmC4H5Gij9zDLMbmrCT2li3eVB72/gF63BPS8pWEZrjlAMRKA8FDw==", + "dependencies": { + "@seald-io/binary-search-tree": "^1.0.3", + "localforage": "^1.9.0", + "util": "^0.12.4" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.2.0.tgz", + "integrity": "sha512-wRlta7GuLWpTqtFfGo+nZyOO1vEvewdNR1R4rTxpC8XU6vG/NDyrFBhwLZsqg1NUoR1noVaXJPC/7ZK47QCySw==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.2.0.tgz", + "integrity": "sha512-fsiMgd8toyUba6n1WRmr+qACzXltpdDkPTAaDqc8QqPBUzO+/JKwL6bUBseHVi8tu9l+3JOK+tSf7cay+4B3LA==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^2.3.0", + "@smithy/types": "^2.12.0", + "@smithy/util-config-provider": "^2.3.0", + "@smithy/util-middleware": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-1.4.0.tgz", + "integrity": "sha512-uu9ZDI95Uij4qk+L6kyFjdk11zqBkcJ3Lv0sc6jZrqHvLyr0+oeekD3CnqMafBn/5PRI6uv6ulW3kNLRBUHeVw==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/middleware-endpoint": "^2.5.0", + "@smithy/middleware-retry": "^2.2.0", + "@smithy/middleware-serde": "^2.3.0", + "@smithy/protocol-http": "^3.3.0", + "@smithy/smithy-client": "^2.5.0", + "@smithy/types": "^2.12.0", + "@smithy/util-middleware": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.3.0.tgz", + "integrity": "sha512-BWB9mIukO1wjEOo1Ojgl6LrG4avcaC7T/ZP6ptmAaW4xluhSIPZhY+/PI5YKzlk+jsm+4sQZB45Bt1OfMeQa3w==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^2.3.0", + "@smithy/property-provider": "^2.2.0", + "@smithy/types": "^2.12.0", + "@smithy/url-parser": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.2.0.tgz", + "integrity": "sha512-8janZoJw85nJmQZc4L8TuePp2pk1nxLgkxIR0TUjKJ5Dkj5oelB9WtiSSGXCQvNsJl0VSTvK/2ueMXxvpa9GVw==", + "optional": true, + "peer": true, + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@smithy/types": "^2.12.0", + "@smithy/util-hex-encoding": "^2.2.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.5.0.tgz", + "integrity": "sha512-BOWEBeppWhLn/no/JxUL/ghTfANTjT7kg3Ww2rPqTUY9R4yHPXxJ9JhMe3Z03LN3aPwiwlpDIUcVw1xDyHqEhw==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/protocol-http": "^3.3.0", + "@smithy/querystring-builder": "^2.2.0", + "@smithy/types": "^2.12.0", + "@smithy/util-base64": "^2.3.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/hash-node": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.2.0.tgz", + "integrity": "sha512-zLWaC/5aWpMrHKpoDF6nqpNtBhlAYKF/7+9yMN7GpdR8CzohnWfGtMznPybnwSS8saaXBMxIGwJqR4HmRp6b3g==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/types": "^2.12.0", + "@smithy/util-buffer-from": "^2.2.0", + "@smithy/util-utf8": "^2.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.2.0.tgz", + "integrity": "sha512-nEDASdbKFKPXN2O6lOlTgrEEOO9NHIeO+HVvZnkqc8h5U9g3BIhWsvzFo+UcUbliMHvKNPD/zVxDrkP1Sbgp8Q==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.2.0.tgz", + "integrity": "sha512-5bl2LG1Ah/7E5cMSC+q+h3IpVHMeOkG0yLRyQT1p2aMJkSrZG7RlXHPuAgb7EyaFeidKEnnd/fNaLLaKlHGzDQ==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/protocol-http": "^3.3.0", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.5.0.tgz", + "integrity": "sha512-OBhI9ZEAG8Xen0xsFJwwNOt44WE2CWkfYIxTognC8x42Lfsdf0VN/wCMqpdkySMDio/vts10BiovAxQp0T0faA==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/middleware-serde": "^2.3.0", + "@smithy/node-config-provider": "^2.3.0", + "@smithy/shared-ini-file-loader": "^2.4.0", + "@smithy/types": "^2.12.0", + "@smithy/url-parser": "^2.2.0", + "@smithy/util-middleware": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.2.0.tgz", + "integrity": "sha512-PsjDOLpbevgn37yJbawmfVoanru40qVA8UEf2+YA1lvOefmhuhL6ZbKtGsLAWDRnE1OlAmedsbA/htH6iSZjNA==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^2.3.0", + "@smithy/protocol-http": "^3.3.0", + "@smithy/service-error-classification": "^2.1.5", + "@smithy/smithy-client": "^2.5.0", + "@smithy/types": "^2.12.0", + "@smithy/util-middleware": "^2.2.0", + "@smithy/util-retry": "^2.2.0", + "tslib": "^2.6.2", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-retry/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "optional": true, + "peer": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.3.0.tgz", + "integrity": "sha512-sIADe7ojwqTyvEQBe1nc/GXB9wdHhi9UwyX0lTyttmUWDJLP655ZYE1WngnNyXREme8I27KCaUhyhZWRXL0q7Q==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.2.0.tgz", + "integrity": "sha512-Qntc3jrtwwrsAC+X8wms8zhrTr0sFXnyEGhZd9sLtsJ/6gGQKFzNB+wWbOcpJd7BR8ThNCoKt76BuQahfMvpeA==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.3.0.tgz", + "integrity": "sha512-0elK5/03a1JPWMDPaS726Iw6LpQg80gFut1tNpPfxFuChEEklo2yL823V94SpTZTxmKlXFtFgsP55uh3dErnIg==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/property-provider": "^2.2.0", + "@smithy/shared-ini-file-loader": "^2.4.0", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.5.0.tgz", + "integrity": "sha512-mVGyPBzkkGQsPoxQUbxlEfRjrj6FPyA3u3u2VXGr9hT8wilsoQdZdvKpMBFMB8Crfhv5dNkKHIW0Yyuc7eABqA==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/abort-controller": "^2.2.0", + "@smithy/protocol-http": "^3.3.0", + "@smithy/querystring-builder": "^2.2.0", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.2.0.tgz", + "integrity": "sha512-+xiil2lFhtTRzXkx8F053AV46QnIw6e7MV8od5Mi68E1ICOjCeCHw2XfLnDEUHnT9WGUIkwcqavXjfwuJbGlpg==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.3.0.tgz", + "integrity": "sha512-Xy5XK1AFWW2nlY/biWZXu6/krgbaf2dg0q492D8M5qthsnU2H+UgFeZLbM76FnH7s6RO/xhQRkj+T6KBO3JzgQ==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.2.0.tgz", + "integrity": "sha512-L1kSeviUWL+emq3CUVSgdogoM/D9QMFaqxL/dd0X7PCNWmPXqt+ExtrBjqT0V7HLN03Vs9SuiLrG3zy3JGnE5A==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/types": "^2.12.0", + "@smithy/util-uri-escape": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.2.0.tgz", + "integrity": "sha512-BvHCDrKfbG5Yhbpj4vsbuPV2GgcpHiAkLeIlcA1LtfpMz3jrqizP1+OguSNSj1MwBHEiN+jwNisXLGdajGDQJA==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.1.5.tgz", + "integrity": "sha512-uBDTIBBEdAQryvHdc5W8sS5YX7RQzF683XrHePVdFmAgKiMofU15FLSM0/HU03hKTnazdNRFa0YHS7+ArwoUSQ==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/types": "^2.12.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.4.0.tgz", + "integrity": "sha512-WyujUJL8e1B6Z4PBfAqC/aGY1+C7T0w20Gih3yrvJSk97gpiVfB+y7c46T4Nunk+ZngLq0rOIdeVeIklk0R3OA==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.2.0.tgz", + "integrity": "sha512-+B5TNzj/fRZzVW3z8UUJOkNx15+4E0CLuvJmJUA1JUIZFp3rdJ/M2H5r2SqltaVPXL0oIxv/6YK92T9TsFGbFg==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/eventstream-codec": "^2.2.0", + "@smithy/is-array-buffer": "^2.2.0", + "@smithy/types": "^2.12.0", + "@smithy/util-hex-encoding": "^2.2.0", + "@smithy/util-middleware": "^2.2.0", + "@smithy/util-uri-escape": "^2.2.0", + "@smithy/util-utf8": "^2.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.5.0.tgz", + "integrity": "sha512-DDXWHWdimtS3y/Kw1Jo46KQ0ZYsDKcldFynQERUGBPDpkW1lXOTHy491ALHjwfiBQvzsVKVxl5+ocXNIgJuX4g==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/middleware-endpoint": "^2.5.0", + "@smithy/middleware-stack": "^2.2.0", + "@smithy/protocol-http": "^3.3.0", + "@smithy/types": "^2.12.0", + "@smithy/util-stream": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.12.0.tgz", + "integrity": "sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.2.0.tgz", + "integrity": "sha512-hoA4zm61q1mNTpksiSWp2nEl1dt3j726HdRhiNgVJQMj7mLp7dprtF57mOB6JvEk/x9d2bsuL5hlqZbBuHQylQ==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/querystring-parser": "^2.2.0", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/util-base64": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.3.0.tgz", + "integrity": "sha512-s3+eVwNeJuXUwuMbusncZNViuhv2LjVJ1nMwTqSA0XAC7gjKhqqxRdJPhR8+YrkoZ9IiIbFk/yK6ACe/xlF+hw==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "@smithy/util-utf8": "^2.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.2.0.tgz", + "integrity": "sha512-dtpw9uQP7W+n3vOtx0CfBD5EWd7EPdIdsQnWTDoFf77e3VUf05uA7R7TGipIo8e4WL2kuPdnsr3hMQn9ziYj5w==", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.3.0.tgz", + "integrity": "sha512-ITWT1Wqjubf2CJthb0BuT9+bpzBfXeMokH/AAa5EJQgbv9aPMVfnM76iFIZVFf50hYXGbtiV71BHAthNWd6+dw==", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@npmcli/config/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/@smithy/util-config-provider": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.3.0.tgz", + "integrity": "sha512-HZkzrRcuFN1k70RLqlNK4FnPXKOpkik1+4JaBoHNJn+RnJGYqaa3c5/+XtLOXhlKzlRgNvyaLieHTW2VwGN0VQ==", + "optional": true, + "peer": true, "dependencies": { - "yallist": "^4.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" + "node": ">=14.0.0" } }, - "node_modules/@npmcli/config/node_modules/nopt": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz", - "integrity": "sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==", + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.2.0.tgz", + "integrity": "sha512-2okTdZaCBvOJszAPU/KSvlimMe35zLOKbQpHhamFJmR7t95HSe0K3C92jQPjKY3PmDBD+7iMkOnuW05F5OlF4g==", + "optional": true, + "peer": true, "dependencies": { - "abbrev": "^2.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" + "@smithy/property-provider": "^2.2.0", + "@smithy/smithy-client": "^2.5.0", + "@smithy/types": "^2.12.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 10.0.0" } }, - "node_modules/@npmcli/config/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/@smithy/util-defaults-mode-node": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.3.0.tgz", + "integrity": "sha512-hfKXnNLmsW9cmLb/JXKIvtuO6Cf4SuqN5PN1C2Ru/TBIws+m1wSgb+A53vo0r66xzB6E82inKG2J7qtwdi+Kkw==", + "optional": true, + "peer": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "@smithy/config-resolver": "^2.2.0", + "@smithy/credential-provider-imds": "^2.3.0", + "@smithy/node-config-provider": "^2.3.0", + "@smithy/property-provider": "^2.2.0", + "@smithy/smithy-client": "^2.5.0", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10" + "node": ">= 10.0.0" } }, - "node_modules/@npmcli/config/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/@npmcli/map-workspaces": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-3.0.4.tgz", - "integrity": "sha512-Z0TbvXkRbacjFFLpVpV0e2mheCh+WzQpcqL+4xp49uNJOxOnIAPZyXtUxZ5Qn3QBTGKA11Exjd9a5411rBrhDg==", + "node_modules/@smithy/util-endpoints": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.2.0.tgz", + "integrity": "sha512-BuDHv8zRjsE5zXd3PxFXFknzBG3owCpjq8G3FcsXW3CykYXuEqM3nTSsmLzw5q+T12ZYuDlVUZKBdpNbhVtlrQ==", + "optional": true, + "peer": true, "dependencies": { - "@npmcli/name-from-folder": "^2.0.0", - "glob": "^10.2.2", - "minimatch": "^9.0.0", - "read-package-json-fast": "^3.0.0" + "@smithy/node-config-provider": "^2.3.0", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 14.0.0" } }, - "node_modules/@npmcli/map-workspaces/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/@smithy/util-hex-encoding": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.2.0.tgz", + "integrity": "sha512-7iKXR+/4TpLK194pVjKiasIyqMtTYJsgKgM242Y9uzt5dhHnUDvMNb+3xIhRJ9QhvqGii/5cRUt4fJn3dtXNHQ==", + "optional": true, + "peer": true, "dependencies": { - "balanced-match": "^1.0.0" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@npmcli/map-workspaces/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "node_modules/@smithy/util-middleware": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.2.0.tgz", + "integrity": "sha512-L1qpleXf9QD6LwLCJ5jddGkgWyuSvWBkJwWAZ6kFkdifdso+sk3L3O1HdmPvCdnCK3IS4qWyPxev01QMnfHSBw==", + "optional": true, + "peer": true, "dependencies": { - "brace-expansion": "^2.0.1" + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=14.0.0" } }, - "node_modules/@npmcli/name-from-folder": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-2.0.0.tgz", - "integrity": "sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg==", + "node_modules/@smithy/util-retry": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.2.0.tgz", + "integrity": "sha512-q9+pAFPTfftHXRytmZ7GzLFFrEGavqapFc06XxzZFcSIGERXMerXxCitjOG1prVDR9QdjqotF40SWvbqcCpf8g==", + "optional": true, + "peer": true, + "dependencies": { + "@smithy/service-error-classification": "^2.1.5", + "@smithy/types": "^2.12.0", + "tslib": "^2.6.2" + }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 14.0.0" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "node_modules/@smithy/util-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.2.0.tgz", + "integrity": "sha512-17faEXbYWIRst1aU9SvPZyMdWmqIrduZjVOqCPMIsWFNxs5yQQgFrJL6b2SdiCzyW9mJoDjFtgi53xx7EH+BXA==", "optional": true, + "peer": true, + "dependencies": { + "@smithy/fetch-http-handler": "^2.5.0", + "@smithy/node-http-handler": "^2.5.0", + "@smithy/types": "^2.12.0", + "@smithy/util-base64": "^2.3.0", + "@smithy/util-buffer-from": "^2.2.0", + "@smithy/util-hex-encoding": "^2.2.0", + "@smithy/util-utf8": "^2.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=14" + "node": ">=14.0.0" } }, - "node_modules/@pkgr/core": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.0.tgz", - "integrity": "sha512-Zwq5OCzuwJC2jwqmpEQt7Ds1DTi6BWSwoGkbb1n9pO3hzb35BoJELx7c0T23iDkBGkh2e7tvOtjF3tr3OaQHDQ==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + "node_modules/@smithy/util-uri-escape": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.2.0.tgz", + "integrity": "sha512-jtmJMyt1xMD/d8OtbVJ2gFZOSKc+ueYJZPW20ULW1GOp/q/YIM0wNh+u8ZFao9UaIGz4WoPW8hC64qlWLIfoDA==", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.6.2" }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/@remix-run/router": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.0.tgz", - "integrity": "sha512-Quz1KOffeEf/zwkCBM3kBtH4ZoZ+pT3xIXBG4PPW/XFtDP7EGhtTiC2+gpL9GnR7+Qdet5Oa6cYSvwKYg6kN9Q==", "engines": { "node": ">=14.0.0" } }, - "node_modules/@seald-io/binary-search-tree": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@seald-io/binary-search-tree/-/binary-search-tree-1.0.3.tgz", - "integrity": "sha512-qv3jnwoakeax2razYaMsGI/luWdliBLHTdC6jU55hQt1hcFqzauH/HsBollQ7IR4ySTtYhT+xyHoijpA16C+tA==" - }, - "node_modules/@seald-io/nedb": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@seald-io/nedb/-/nedb-4.0.4.tgz", - "integrity": "sha512-CUNcMio7QUHTA+sIJ/DC5JzVNNsHe743TPmC4H5Gij9zDLMbmrCT2li3eVB72/gF63BPS8pWEZrjlAMRKA8FDw==", + "node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "optional": true, + "peer": true, "dependencies": { - "@seald-io/binary-search-tree": "^1.0.3", - "localforage": "^1.9.0", - "util": "^0.12.4" + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" } }, "node_modules/@types/babel__core": { @@ -2092,12 +3156,6 @@ "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==" }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, "node_modules/@types/node": { "version": "20.10.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.7.tgz", @@ -2244,6 +3302,17 @@ "node": ">=4.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -2327,12 +3396,29 @@ "node": ">=8" } }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, "node_modules/archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", "dev": true }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2416,25 +3502,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", - "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/array.prototype.flat": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", @@ -2531,6 +3598,17 @@ "safer-buffer": "~2.1.0" } }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, "node_modules/assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", @@ -2618,6 +3696,19 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -2638,6 +3729,11 @@ "node": "*" } }, + "node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, "node_modules/body-parser": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", @@ -2674,11 +3770,17 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "optional": true, + "peer": true + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2742,65 +3844,6 @@ "node": ">=14.20.1" } }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/builtins": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", - "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", - "dev": true, - "peer": true, - "dependencies": { - "semver": "^7.0.0" - } - }, - "node_modules/builtins/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "peer": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/builtins/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "peer": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/builtins/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "peer": true - }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -2952,14 +3995,6 @@ "node": ">=4.0.0" } }, - "node_modules/chartist": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/chartist/-/chartist-0.10.1.tgz", - "integrity": "sha512-8Yd4F6tXvAaM5CIPmFYknC76KEgaU7iiY7Amo5IaaCRmTT/5YXjc/FuNXgqhPttOXHoGSjl0i3gkOE1mv15/wA==", - "engines": { - "node": ">=4.6.0" - } - }, "node_modules/check-error": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", @@ -3011,6 +4046,14 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, "node_modules/ci-info": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", @@ -3139,6 +4182,14 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -3177,8 +4228,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/concurrently": { "version": "8.2.2", @@ -3284,6 +4334,27 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/connect-mongo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/connect-mongo/-/connect-mongo-5.1.0.tgz", + "integrity": "sha512-xT0vxQLqyqoUTxPLzlP9a/u+vir0zNkhiy9uAdHjSCcUUf7TS5b55Icw8lVyYFxfemP3Mf9gdwUOgeF3cxCAhw==", + "dependencies": { + "debug": "^4.3.1", + "kruptein": "^3.0.0" + }, + "engines": { + "node": ">=12.9.0" + }, + "peerDependencies": { + "express-session": "^1.17.1", + "mongodb": ">= 5.1.0 < 7" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -3591,6 +4662,11 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -3608,6 +4684,14 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "engines": { + "node": ">=8" + } + }, "node_modules/dezalgo": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", @@ -3744,14 +4828,6 @@ "integrity": "sha512-lKoz10iCYlP1WtRYdh5MvocQPWVRoI7ysp6qf18bmeBgR8abE6+I2CsfyNKztRDZvhdWc+krKT6wS7Neg8sw3A==", "dev": true }, - "node_modules/email-validator": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/email-validator/-/email-validator-2.0.4.tgz", - "integrity": "sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ==", - "engines": { - "node": ">4.0" - } - }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -4053,19 +5129,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-compat-utils": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz", - "integrity": "sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "eslint": ">=6.0.0" - } - }, "node_modules/eslint-config-google": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.14.0.tgz", @@ -4090,272 +5153,17 @@ "eslint": ">=7.0.0" } }, - "node_modules/eslint-config-standard": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", - "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "eslint": "^8.0.1", - "eslint-plugin-import": "^2.25.2", - "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", - "eslint-plugin-promise": "^6.0.0" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", - "dev": true, - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", - "dev": true, - "dependencies": { - "eslint-utils": "^2.0.0", - "regexpp": "^3.0.0" - }, - "engines": { - "node": ">=8.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=4.19.1" - } - }, - "node_modules/eslint-plugin-es-x": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.5.0.tgz", - "integrity": "sha512-ODswlDSO0HJDzXU0XvgZ3lF3lS3XAZEossh15Q2UHjwrJggWeBoKqqEsLTZLXl+dh5eOAozG0zRcYtuE35oTuQ==", - "dev": true, - "peer": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.1.2", - "@eslint-community/regexpp": "^4.6.0", - "eslint-compat-utils": "^0.1.2" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - }, - "peerDependencies": { - "eslint": ">=8" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", - "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", - "semver": "^6.3.1", - "tsconfig-paths": "^3.15.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-n": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.1.tgz", - "integrity": "sha512-M1kE5bVQRLBMDYRZwDhWzlzbp370SRRRC1MHqq4I3L2Tatey+9/2csc5mwLDPlmhJaDvkojbrNUME5/llpRyDg==", - "dev": true, - "peer": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "builtins": "^5.0.1", - "eslint-plugin-es-x": "^7.5.0", - "get-tsconfig": "^4.7.0", - "globals": "^13.24.0", - "ignore": "^5.2.4", - "is-builtin-module": "^3.2.1", - "is-core-module": "^2.12.1", - "minimatch": "^3.1.2", - "resolve": "^1.22.2", - "semver": "^7.5.3" - }, - "engines": { - "node": ">=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-n/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "peer": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-plugin-n/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "peer": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-plugin-n/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "peer": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-plugin-n/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "peer": true - }, - "node_modules/eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "node_modules/eslint-plugin-json": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-json/-/eslint-plugin-json-3.1.0.tgz", + "integrity": "sha512-MrlG2ynFEHe7wDGwbUuFPsaT2b1uhuEFhJ+W1f1u+1C2EkXmTYJp4B1aAdQQ8M+CC3t//N/oRKiIVw14L2HR1g==", "dev": true, "dependencies": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", - "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" + "lodash": "^4.17.21", + "vscode-json-languageservice": "^4.1.6" }, "engines": { - "node": ">=8.10.0" - }, - "peerDependencies": { - "eslint": ">=5.16.0" + "node": ">=12.0" } }, "node_modules/eslint-plugin-prettier": { @@ -4388,18 +5196,6 @@ } } }, - "node_modules/eslint-plugin-promise": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", - "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, "node_modules/eslint-plugin-react": { "version": "7.34.1", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz", @@ -4507,30 +5303,6 @@ "node": ">=4.0" } }, - "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/eslint-visitor-keys": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", @@ -4999,6 +5771,29 @@ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, + "node_modules/fast-xml-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", + "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", + "funding": [ + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + }, + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "optional": true, + "peer": true, + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { "version": "1.16.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", @@ -5240,11 +6035,37 @@ } ] }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", @@ -5295,10 +6116,42 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/generate-password": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/generate-password/-/generate-password-1.7.1.tgz", - "integrity": "sha512-9bVYY+16m7W7GczRBDqXE+VVuCX+bWNrfYKC/2p2JkZukFb2sKxT6E3zZ3mJGz7GMe5iRK0A/WawSL3jQfJuNQ==" + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gauge/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/gauge/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } }, "node_modules/gensync": { "version": "1.0.0-beta.2", @@ -5382,19 +6235,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-tsconfig": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", - "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", - "dev": true, - "peer": true, - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, "node_modules/git-raw-commits": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", @@ -5594,6 +6434,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, "node_modules/hasha": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", @@ -5723,6 +6568,18 @@ "node": ">= 0.8" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/human-signals": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", @@ -5833,7 +6690,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -5979,22 +6835,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-builtin-module": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", - "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", - "dev": true, - "peer": true, - "dependencies": { - "builtin-modules": "^3.3.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -6623,6 +7463,12 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "dev": true + }, "node_modules/jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", @@ -6767,6 +7613,17 @@ "json-buffer": "3.0.1" } }, + "node_modules/kruptein": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/kruptein/-/kruptein-3.0.6.tgz", + "integrity": "sha512-EQJjTwAJfQkC4NfdQdo3HXM2a9pmBm8oidzH270cYu1MbgXPNPMJuldN7OPX+qdhPO5rw4X3/iKz0BFBfkXGKA==", + "dependencies": { + "asn1.js": "^5.4.1" + }, + "engines": { + "node": ">8" + } + }, "node_modules/ldap-filter": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.3.3.tgz", @@ -7054,7 +7911,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, "dependencies": { "semver": "^6.0.0" }, @@ -7171,11 +8027,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -7200,6 +8060,34 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/mkdirp": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", @@ -7510,6 +8398,49 @@ "node": ">= 0.6" } }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", @@ -7594,6 +8525,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, "node_modules/nyc": { "version": "15.1.0", "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", @@ -7919,18 +8861,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.groupby": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", - "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1" - } - }, "node_modules/object.hasown": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", @@ -8185,17 +9115,6 @@ "node": ">= 0.4.0" } }, - "node_modules/password-hash": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/password-hash/-/password-hash-1.2.2.tgz", - "integrity": "sha512-Dy/5+Srojwv+1XnMrK2bn7f2jN3k2p90DfBVA0Zd6PrjWF7lXHOTWgKT4uBp1gIsqV7/llYqm+hj+gwDBF/Fmg==", - "bin": { - "nodepw": "bin/nodepw" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -8209,7 +9128,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -8270,6 +9188,11 @@ "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" }, + "node_modules/perfect-scrollbar": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.5.5.tgz", + "integrity": "sha512-dzalfutyP3e/FOpdlhVryN4AJ5XDVauVWxybSkLZmakFE2sS3y3pc4JnSprw8tGmHvkaG5Edr5T7LBTZ+WWU2g==" + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -8601,18 +9524,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-chartist": { - "version": "0.14.4", - "resolved": "https://registry.npmjs.org/react-chartist/-/react-chartist-0.14.4.tgz", - "integrity": "sha512-1riwO5CjT5IdrilCM788waUYpIdl8Y4vsIc6Y5bbR3PIX46NYYtcq5bcaBEkIZfKEThuoJDEV6FU1rDmfbogsg==", - "dependencies": { - "prop-types": "^15.5.8" - }, - "peerDependencies": { - "chartist": "^0.10.1", - "react": "^0.14.9 || ^15.0.0 || ^16.0.0 || ^17.0.0" - } - }, "node_modules/react-dom": { "version": "16.14.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", @@ -8785,18 +9696,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, "node_modules/release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -8832,23 +9731,6 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -8858,16 +9740,6 @@ "node": ">=8" } }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "peer": true, - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -8882,7 +9754,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -8897,7 +9768,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -9032,7 +9902,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "bin": { "semver": "bin/semver.js" } @@ -9104,8 +9973,7 @@ "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, "node_modules/set-function-length": { "version": "1.2.1", @@ -9190,8 +10058,7 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/smart-buffer": { "version": "4.2.0", @@ -9456,15 +10323,6 @@ "node": ">=8" } }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", @@ -9489,6 +10347,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "optional": true, + "peer": true + }, "node_modules/superagent": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", @@ -9590,6 +10455,46 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -9701,30 +10606,6 @@ "tree-kill": "cli.js" } }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -10106,6 +10987,43 @@ } } }, + "node_modules/vscode-json-languageservice": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.2.1.tgz", + "integrity": "sha512-xGmv9QIWs2H8obGbWg+sIPI/3/pFgj/5OWBhNzs00BkYQ9UaB2F6JJaGB/2/YOZJ3BvLXQTC4Q7muqU25QgAhA==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.0.0", + "vscode-languageserver-textdocument": "^1.0.3", + "vscode-languageserver-types": "^3.16.0", + "vscode-nls": "^5.0.0", + "vscode-uri": "^3.0.3" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz", + "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==", + "dev": true + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "dev": true + }, + "node_modules/vscode-nls": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz", + "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==", + "dev": true + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "dev": true + }, "node_modules/walk-up-path": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-3.0.1.tgz", @@ -10226,6 +11144,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wide-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/workerpool": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", diff --git a/package.json b/package.json index 2aa087a0..b17c9cfd 100644 --- a/package.json +++ b/package.json @@ -9,41 +9,43 @@ "server": "node index.js", "start": "concurrently 'npm run server' 'npm run client'", "build": "vite build", - "ui-test": "vite", - "server-test-ci": "mocha --exit", - "server-test": "mocha --exit", - "test": "mocha --exit", + "test": "NODE_ENV=test mocha --exit", "test-coverage": "nyc npm run test", "test-coverage-ci": "nyc --reporter=lcovonly --reporter=text npm run test", "prepare": "node ./scripts/prepare.js", - "lint": "eslint --fix . --ext .js,.jsx", + "lint": "eslint \"src/**/*.{js,jsx,ts,tsx,json}\" \"test/**/*.{js,jsx,ts,tsx,json}\"", + "lint:fix": "eslint --fix \"src/**/*.{js,jsx,ts,tsx,json}\" \"test/**/*.{js,jsx,ts,tsx,json}\"", + "format": "prettier --write src/**/*.{js,jsx,ts,tsx,css,md,json,scss} test/**/*.{js,jsx,ts,tsx,json} --config ./.prettierrc", "gen-schema-doc": "node ./scripts/doc-schema.js" }, + "bin": { + "git-proxy": "./index.js", + "git-proxy-all": "concurrently 'npm run server' 'npm run client'" + }, "workspaces": [ "./packages/git-proxy-cli" ], - "bin": "./index.js", "author": "Paul Groves", "license": "Apache-2.0", "dependencies": { "@material-ui/core": "^4.11.0", "@material-ui/icons": "4.11.3", + "@primer/octicons-react": "^19.8.0", "@seald-io/nedb": "^4.0.2", "axios": "^1.6.0", + "bcrypt": "^5.1.1", "bit-mask": "^1.0.2", "body-parser": "^1.20.1", "chai-http": "^4.3.0", - "chartist": "0.10.1", "classnames": "2.5.1", "concurrently": "^8.0.0", + "connect-mongo": "^5.1.0", "cors": "^2.8.5", "diff2html": "^3.4.33", - "email-validator": "^2.0.4", "express": "^4.18.2", "express-http-proxy": "^2.0.0", "express-rate-limit": "^7.1.5", "express-session": "^1.17.1", - "generate-password": "^1.5.1", "history": "5.3.0", "jsonschema": "^1.4.1", "load-plugin": "^6.0.0", @@ -55,10 +57,9 @@ "passport": "^0.7.0", "passport-activedirectory": "^1.0.4", "passport-local": "^1.0.0", - "password-hash": "^1.2.2", + "perfect-scrollbar": "^1.5.5", "prop-types": "15.8.1", "react": "^16.13.1", - "react-chartist": "0.14.4", "react-dom": "^16.13.1", "react-html-parser": "^2.0.2", "react-router-dom": "6.23.0", @@ -66,20 +67,18 @@ "yargs": "^17.7.2" }, "devDependencies": { + "@babel/core": "^7.23.2", "@babel/eslint-parser": "^7.22.9", "@babel/preset-react": "^7.22.5", "@commitlint/cli": "^19.0.0", "@commitlint/config-conventional": "^19.0.0", "@vitejs/plugin-react": "^4.0.2", "chai": "^4.2.0", - "eslint": "^8.0.0", + "eslint": "^8.57.0", "eslint-config-google": "^0.14.0", "eslint-config-prettier": "^9.0.0", - "eslint-config-standard": "^17.0.0", - "eslint-plugin-import": "^2.22.1", - "eslint-plugin-node": "^11.1.0", + "eslint-plugin-json": "^3.1.0", "eslint-plugin-prettier": "^5.0.0", - "eslint-plugin-promise": "^6.0.0", "eslint-plugin-react": "^7.21.5", "eslint-plugin-standard": "^5.0.0", "husky": "^9.0.0", diff --git a/packages/git-proxy-cli/index.js b/packages/git-proxy-cli/index.js index 0e02e028..270f2b45 100755 --- a/packages/git-proxy-cli/index.js +++ b/packages/git-proxy-cli/index.js @@ -8,8 +8,7 @@ const util = require('util'); const GIT_PROXY_COOKIE_FILE = 'git-proxy-cookie'; // GitProxy UI HOST and PORT (configurable via environment variable) const { GIT_PROXY_UI_HOST: uiHost = 'http://localhost' } = process.env; -const { GIT_PROXY_UI_PORT: uiPort } = - require('@finos/git-proxy/src/config/env').Vars; +const { GIT_PROXY_UI_PORT: uiPort } = require('@finos/git-proxy/src/config/env').Vars; const baseUrl = `${uiHost}:${uiPort}`; axios.defaults.timeout = 30000; @@ -22,7 +21,7 @@ axios.defaults.timeout = 30000; async function login(username, password) { try { let response = await axios.post( - `${baseUrl}/auth/login`, + `${baseUrl}/api/auth/login`, { username, password, @@ -34,7 +33,7 @@ async function login(username, password) { ); const cookies = response.headers['set-cookie']; - response = await axios.get(`${baseUrl}/auth/profile`, { + response = await axios.get(`${baseUrl}/api/auth/profile`, { headers: { Cookie: cookies }, withCredentials: true, }); @@ -126,13 +125,8 @@ async function getGitPushes(filters) { console.log(`${util.inspect(records, false, null, false)}`); } catch (error) { // default error - let errorMessage = `Error: List: '${error.message}'`; + const errorMessage = `Error: List: '${error.message}'`; process.exitCode = 2; - - if (error.response && error.response.status == 401) { - errorMessage = 'Error: List: Authentication required'; - process.exitCode = 3; - } console.error(errorMessage); } } @@ -151,13 +145,22 @@ async function authoriseGitPush(id) { try { const cookies = JSON.parse(fs.readFileSync(GIT_PROXY_COOKIE_FILE, 'utf8')); - response = await axios.get(`${baseUrl}/api/v1/push/${id}`, { + await axios.get(`${baseUrl}/api/v1/push/${id}`, { headers: { Cookie: cookies }, }); - response = await axios.post( + await axios.post( `${baseUrl}/api/v1/push/${id}/authorise`, - {}, + { + params: { + attestation: [ + { + label: "Authorising via GitProxy CLI", + checked: true + } + ] + } + }, { headers: { Cookie: cookies }, }, @@ -198,11 +201,11 @@ async function rejectGitPush(id) { try { const cookies = JSON.parse(fs.readFileSync(GIT_PROXY_COOKIE_FILE, 'utf8')); - response = await axios.get(`${baseUrl}/api/v1/push/${id}`, { + await axios.get(`${baseUrl}/api/v1/push/${id}`, { headers: { Cookie: cookies }, }); - response = await axios.post( + await axios.post( `${baseUrl}/api/v1/push/${id}/reject`, {}, { @@ -245,11 +248,11 @@ async function cancelGitPush(id) { try { const cookies = JSON.parse(fs.readFileSync(GIT_PROXY_COOKIE_FILE, 'utf8')); - response = await axios.get(`${baseUrl}/api/v1/push/${id}`, { + await axios.get(`${baseUrl}/api/v1/push/${id}`, { headers: { Cookie: cookies }, }); - response = await axios.post( + await axios.post( `${baseUrl}/api/v1/push/${id}/cancel`, {}, { @@ -284,14 +287,12 @@ async function cancelGitPush(id) { async function logout() { if (fs.existsSync(GIT_PROXY_COOKIE_FILE)) { try { - const cookies = JSON.parse( - fs.readFileSync(GIT_PROXY_COOKIE_FILE, 'utf8'), - ); + const cookies = JSON.parse(fs.readFileSync(GIT_PROXY_COOKIE_FILE, 'utf8')); fs.writeFileSync(GIT_PROXY_COOKIE_FILE, '*** logged out ***', 'utf8'); fs.unlinkSync(GIT_PROXY_COOKIE_FILE); - response = await axios.post( - `${baseUrl}/auth/logout`, + await axios.post( + `${baseUrl}/api/auth/logout`, {}, { headers: { Cookie: cookies }, diff --git a/packages/git-proxy-cli/package.json b/packages/git-proxy-cli/package.json index 8cb33729..615305d3 100644 --- a/packages/git-proxy-cli/package.json +++ b/packages/git-proxy-cli/package.json @@ -13,7 +13,7 @@ }, "scripts": { "lint": "eslint --fix . --ext .js,.jsx", - "test": "mocha --exit --timeout 10000", + "test": "NODE_ENV=test mocha --exit --timeout 10000", "test-coverage": "nyc npm run test", "test-coverage-ci": "nyc --reporter=lcovonly --reporter=text --reporter=html npm run test" }, diff --git a/packages/git-proxy-cli/test/testCli.test.js b/packages/git-proxy-cli/test/testCli.test.js index 23438786..0474bad8 100644 --- a/packages/git-proxy-cli/test/testCli.test.js +++ b/packages/git-proxy-cli/test/testCli.test.js @@ -15,6 +15,13 @@ const service = require('../../../src/service'); // push ID which does not exist const GHOST_PUSH_ID = '0000000000000000000000000000000000000000__79b4d8953cbc324bcc1eb53d6412ff89666c241f'; +// repo for test cases +const TEST_REPO_CONFIG = { + project: 'finos', + name: 'git-proxy-test', + url: 'https://github.com/finos/git-proxy-test.git' +} +const TEST_REPO = 'finos/git-proxy-test.git'; describe('test git-proxy-cli', function () { // *** help *** @@ -280,6 +287,13 @@ describe('test git-proxy-cli', function () { // *** authorise *** describe('test git-proxy-cli :: authorise', function () { + const pushId = `auth000000000000000000000000000000000000__${Date.now()}`; + + before(async function() { + await helper.addRepoToDb(TEST_REPO_CONFIG); + await helper.addGitPushToDb(pushId, TEST_REPO); + }) + it('attempt to authorise should fail when server is down', async function () { try { // start server -> login -> stop server @@ -326,7 +340,7 @@ describe('test git-proxy-cli', function () { try { await helper.createCookiesFileWithExpiredCookie(); await helper.startServer(service); - const id = GHOST_PUSH_ID; + const id = pushId; const cli = `npx -- @finos/git-proxy-cli authorise --id ${id}`; const expectedExitCode = 3; const expectedMessages = null; @@ -373,6 +387,13 @@ describe('test git-proxy-cli', function () { // *** cancel *** describe('test git-proxy-cli :: cancel', function () { + const pushId = `cancel0000000000000000000000000000000000__${Date.now()}`; + + before(async function() { + await helper.addRepoToDb(TEST_REPO_CONFIG); + await helper.addGitPushToDb(pushId, TEST_REPO); + }) + it('attempt to cancel should fail when server is down', async function () { try { // start server -> login -> stop server @@ -417,7 +438,7 @@ describe('test git-proxy-cli', function () { try { await helper.createCookiesFileWithExpiredCookie(); await helper.startServer(service); - const id = GHOST_PUSH_ID; + const id = pushId; const cli = `npx -- @finos/git-proxy-cli cancel --id ${id}`; const expectedExitCode = 3; const expectedMessages = null; @@ -501,25 +522,6 @@ describe('test git-proxy-cli', function () { ); }); - it('attempt to ls should fail when not authenticated (server restarted)', async function () { - try { - await helper.createCookiesFileWithExpiredCookie(); - await helper.startServer(service); - const cli = `npx -- @finos/git-proxy-cli ls`; - const expectedExitCode = 3; - const expectedMessages = null; - const expectedErrorMessages = ['Error: List: Authentication required']; - await helper.runCli( - cli, - expectedExitCode, - expectedMessages, - expectedErrorMessages, - ); - } finally { - await helper.closeServer(service.httpServer); - } - }); - it('attempt to ls should fail when invalid option given', async function () { try { await helper.startServer(service); @@ -546,6 +548,13 @@ describe('test git-proxy-cli', function () { // *** reject *** describe('test git-proxy-cli :: reject', function () { + const pushId = `reject0000000000000000000000000000000000__${Date.now()}`; + + before(async function() { + await helper.addRepoToDb(TEST_REPO_CONFIG); + await helper.addGitPushToDb(pushId, TEST_REPO); + }) + it('attempt to reject should fail when server is down', async function () { try { // start server -> login -> stop server @@ -590,7 +599,7 @@ describe('test git-proxy-cli', function () { try { await helper.createCookiesFileWithExpiredCookie(); await helper.startServer(service); - const id = GHOST_PUSH_ID; + const id = pushId; const cli = `npx -- @finos/git-proxy-cli reject --id ${id}`; const expectedExitCode = 3; const expectedMessages = null; @@ -636,10 +645,10 @@ describe('test git-proxy-cli', function () { describe('test git-proxy-cli :: git push administration', function () { const pushId = `0000000000000000000000000000000000000000__${Date.now()}`; - const repo = 'test-repo'; before(async function () { - await helper.addGitPushToDb(pushId, repo); + await helper.addRepoToDb(TEST_REPO_CONFIG); + await helper.addGitPushToDb(pushId, TEST_REPO); }); it('attempt to ls should list existing push', async function () { @@ -653,7 +662,7 @@ describe('test git-proxy-cli', function () { const expectedExitCode = 0; const expectedMessages = [ pushId, - repo, + TEST_REPO, 'authorised: false', 'blocked: true', 'canceled: false', @@ -791,7 +800,7 @@ describe('test git-proxy-cli', function () { cli = `npx -- @finos/git-proxy-cli ls --authorised true --canceled false --rejected false`; expectedExitCode = 0; - expectedMessages = [pushId, repo]; + expectedMessages = [pushId, TEST_REPO]; expectedErrorMessages = null; await helper.runCli( cli, @@ -835,7 +844,7 @@ describe('test git-proxy-cli', function () { cli = `npx -- @finos/git-proxy-cli ls --authorised false --canceled false --rejected true`; expectedExitCode = 0; - expectedMessages = [pushId, repo]; + expectedMessages = [pushId, TEST_REPO]; expectedErrorMessages = null; await helper.runCli( cli, @@ -879,7 +888,7 @@ describe('test git-proxy-cli', function () { cli = `npx -- @finos/git-proxy-cli ls --authorised false --canceled true --rejected false`; expectedExitCode = 0; - expectedMessages = [pushId, repo]; + expectedMessages = [pushId, TEST_REPO]; expectedErrorMessages = null; await helper.runCli( cli, diff --git a/packages/git-proxy-cli/test/testCliUtils.js b/packages/git-proxy-cli/test/testCliUtils.js index 376ecea4..fb022320 100644 --- a/packages/git-proxy-cli/test/testCliUtils.js +++ b/packages/git-proxy-cli/test/testCliUtils.js @@ -33,7 +33,7 @@ async function runCli( debug = false, ) { try { - console.log(`cli: ${cli}`); + console.log(`cli: '${cli}'`); const { stdout, stderr } = await execAsync(cli); if (debug) { console.log(`stdout: ${stdout}`); @@ -72,6 +72,10 @@ async function runCli( expect(error.stderr).to.include(expectedErrorMessage); }); } + } finally { + if (debug) { + console.log(`cli: '${cli}': done`); + } } } @@ -138,6 +142,28 @@ async function removeCookiesFile() { } } +/** + * Add a new repo to the database. + * @param {object} newRepo The new repo attributes. + * @param {boolean} debug Print debug messages to console if true. + */ +async function addRepoToDb(newRepo, debug = false) { + const repos = await db.getRepos(); + const found = repos.find((y) => y.project === newRepo.project && newRepo.name === y.name); + if (!found) { + await db.createRepo(newRepo); + await db.addUserCanPush(newRepo.name, 'admin'); + await db.addUserCanAuthorise(newRepo.name, 'admin'); + if (debug) { + console.log(`New repo added to database: ${newRepo}`); + } + } else { + if (debug) { + console.log(`New repo already found in database: ${newRepo}`); + } + } +} + /** * Add a new git push record to the database. * @param {string} id The ID of the git push. @@ -210,6 +236,7 @@ module.exports = { runCli: runCli, startServer: startServer, closeServer: closeServer, + addRepoToDb: addRepoToDb, addGitPushToDb: addGitPushToDb, addUserToDb: addUserToDb, createCookiesFileWithExpiredCookie: createCookiesFileWithExpiredCookie, diff --git a/proxy.config.json b/proxy.config.json index 683fdf7d..02f390a8 100644 --- a/proxy.config.json +++ b/proxy.config.json @@ -1,8 +1,10 @@ { + "proxyUrl": "https://github.com", + "cookieSecret": "cookie secret", + "sessionMaxAgeHours": 12, "tempPassword": { "sendEmail": false, - "emailConfig": { - } + "emailConfig": {} }, "authorisedList": [ { @@ -22,9 +24,12 @@ { "type": "mongo", "connectionString": "mongodb://localhost:27017/gitproxy", - "options": { - "useUnifiedTopology": true - }, + "options": { + "useNewUrlParser": true, + "useUnifiedTopology": true, + "tlsAllowInvalidCertificates": false, + "ssl": true + }, "enabled": false } ], @@ -32,6 +37,63 @@ { "type": "local", "enabled": true + }, + { + "type": "ActiveDirectory", + "enabled": false, + "adminGroup": "", + "userGroup": "", + "domain": "", + "adConfig": { + "url": "", + "baseDN": "", + "searchBase": "" + } + } + ], + "api": { + "github": { + "baseUrl": "https://api.github.com" } - ] + }, + "commitConfig": { + "author": { + "email": { + "local": { + "block": "" + }, + "domain": { + "allow": ".*" + } + } + }, + "message": { + "block": { + "literals": [], + "patterns": [] + } + }, + "diff": { + "block": { + "literals": [], + "patterns": [], + "providers": {} + } + } + }, + "attestationConfig": { + "questions": [ + { + "label": "I am happy for this to be pushed to the upstream repository", + "tooltip": { + "text": "Are you happy for this contribution to be pushed upstream?", + "links": [] + } + } + ] + }, + "privateOrganizations": [], + "urlShortener": "", + "contactEmail": "", + "csrfProtection": true } diff --git a/src/config/file.js b/src/config/file.js index 8779e604..1cc99abb 100644 --- a/src/config/file.js +++ b/src/config/file.js @@ -23,9 +23,7 @@ function validate(configFilePath = configFile) { module.exports = { get configFile() { - return configFile - ? configFile - : path.join(process.cwd(), 'proxy.config.json'); + return configFile ? configFile : path.join(process.cwd(), 'proxy.config.json'); }, set configFile(file) { configFile = file; diff --git a/src/config/index.js b/src/config/index.js index e94f38c9..1708fada 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -11,6 +11,25 @@ let _authorisedList = defaultSettings.authorisedList; let _database = defaultSettings.sink; let _authentication = defaultSettings.authentication; let _tempPassword = defaultSettings.tempPassword; +let _proxyUrl = defaultSettings.proxyUrl; +let _api = defaultSettings.api; +let _cookieSecret = defaultSettings.cookieSecret; +let _sessionMaxAgeHours = defaultSettings.sessionMaxAgeHours; +const _commitConfig = defaultSettings.commitConfig; +const _attestationConfig = defaultSettings.attestationConfig; +const _privateOrganizations = defaultSettings.privateOrganizations; +const _urlShortener = defaultSettings.urlShortener; +const _contactEmail = defaultSettings.contactEmail; +const _csrfProtection = defaultSettings.csrfProtection; + +// Get configured proxy URL +const getProxyUrl = () => { + if (_userSettings !== null && _userSettings.proxyUrl) { + _proxyUrl = _userSettings.proxyUrl; + } + + return _proxyUrl; +}; // Gets a list of authorised repositories const getAuthorisedList = () => { @@ -70,10 +89,69 @@ const logConfiguration = () => { console.log(`authentication = ${JSON.stringify(getAuthentication())}`); }; -// logConfiguration(); +const getAPIs = () => { + if (_userSettings && _userSettings.api) { + _api = _userSettings.api; + } + return _api; +}; + +const getCookieSecret = () => { + if (_userSettings && _userSettings.cookieSecret) { + _cookieSecret = _userSettings.cookieSecret; + } + return _cookieSecret; +}; + +const getSessionMaxAgeHours = () => { + if (_userSettings && _userSettings.sessionMaxAgeHours) { + _sessionMaxAgeHours = _userSettings.sessionMaxAgeHours; + } + return _sessionMaxAgeHours; +}; + +// Get commit related configuration +const getCommitConfig = () => { + return _commitConfig; +}; + +// Get attestation related configuration +const getAttestationConfig = () => { + return _attestationConfig; +}; + +// Get private organizations related configuration +const getPrivateOrganizations = () => { + return _privateOrganizations; +}; + +// Get URL shortener +const getURLShortener = () => { + return _urlShortener; +}; + +// Get contact e-mail address +const getContactEmail = () => { + return _contactEmail; +}; + +// Get CSRF protection flag +const getCSRFProtection = () => { + return _csrfProtection; +}; +exports.getAPIs = getAPIs; +exports.getProxyUrl = getProxyUrl; exports.getAuthorisedList = getAuthorisedList; exports.getDatabase = getDatabase; exports.logConfiguration = logConfiguration; exports.getAuthentication = getAuthentication; exports.getTempPasswordConfig = getTempPasswordConfig; +exports.getCookieSecret = getCookieSecret; +exports.getSessionMaxAgeHours = getSessionMaxAgeHours; +exports.getCommitConfig = getCommitConfig; +exports.getAttestationConfig = getAttestationConfig; +exports.getPrivateOrganizations = getPrivateOrganizations; +exports.getURLShortener = getURLShortener; +exports.getContactEmail = getContactEmail; +exports.getCSRFProtection = getCSRFProtection; diff --git a/src/context.js b/src/context.js new file mode 100644 index 00000000..40223ff1 --- /dev/null +++ b/src/context.js @@ -0,0 +1,3 @@ +import { createContext } from 'react'; + +export const UserContext = createContext(null); diff --git a/src/db/file/index.js b/src/db/file/index.js index 583be503..f77833f5 100644 --- a/src/db/file/index.js +++ b/src/db/file/index.js @@ -8,6 +8,9 @@ module.exports.getPush = pushes.getPush; module.exports.authorise = pushes.authorise; module.exports.cancel = pushes.cancel; module.exports.reject = pushes.reject; +module.exports.canUserCancelPush = pushes.canUserCancelPush; +module.exports.canUserApproveRejectPush = pushes.canUserApproveRejectPush; + module.exports.findUser = users.findUser; module.exports.getUsers = users.getUsers; module.exports.createUser = users.createUser; @@ -21,5 +24,6 @@ module.exports.addUserCanPush = repo.addUserCanPush; module.exports.addUserCanAuthorise = repo.addUserCanAuthorise; module.exports.removeUserCanPush = repo.removeUserCanPush; module.exports.removeUserCanAuthorise = repo.removeUserCanAuthorise; - module.exports.deleteRepo = repo.deleteRepo; +module.exports.isUserPushAllowed = repo.isUserPushAllowed; +module.exports.canUserApproveRejectPushRepo = repo.canUserApproveRejectPushRepo; diff --git a/src/db/file/pushes.js b/src/db/file/pushes.js index eac6d51a..cbc79244 100644 --- a/src/db/file/pushes.js +++ b/src/db/file/pushes.js @@ -3,6 +3,7 @@ const _ = require('lodash'); const Datastore = require('@seald-io/nedb'); const Action = require('../../proxy/actions/Action').Action; const toClass = require('../helper').toClass; +const repo = require('./repo'); if (!fs.existsSync('./.data')) fs.mkdirSync('./.data'); if (!fs.existsSync('./.data/db')) fs.mkdirSync('./.data/db'); @@ -62,11 +63,12 @@ const writeAudit = async (action, logger) => { }); }; -const authorise = async (id, logger) => { - const action = await getPush(id, logger); +const authorise = async (id, attestation) => { + const action = await getPush(id); action.authorised = true; action.canceled = false; action.rejected = false; + action.attestation = attestation; await writeAudit(action); return { message: `authorised ${id}` }; }; @@ -89,9 +91,35 @@ const cancel = async (id, logger) => { return { message: `cancel ${id}` }; }; +const canUserCancelPush = async (id, user) => { + return new Promise(async (resolve) => { + const pushDetail = await getPush(id); + const repoName = pushDetail.repoName.replace('.git', ''); + const isAllowed = await repo.isUserPushAllowed(repoName, user); + + if (isAllowed) { + resolve(true); + } else { + resolve(false); + } + }); +}; + +const canUserApproveRejectPush = async (id, user) => { + return new Promise(async (resolve) => { + const action = await getPush(id); + const repoName = action.repoName.replace('.git', ''); + const isAllowed = await repo.canUserApproveRejectPushRepo(repoName, user); + + resolve(isAllowed); + }); +}; + module.exports.getPushes = getPushes; module.exports.writeAudit = writeAudit; module.exports.getPush = getPush; module.exports.authorise = authorise; module.exports.reject = reject; module.exports.cancel = cancel; +module.exports.canUserCancelPush = canUserCancelPush; +module.exports.canUserApproveRejectPush = canUserApproveRejectPush; diff --git a/src/db/file/repo.js b/src/db/file/repo.js index 612d256b..e6f7dd89 100644 --- a/src/db/file/repo.js +++ b/src/db/file/repo.js @@ -139,3 +139,33 @@ exports.deleteRepo = async (name) => { }); }); }; + +exports.isUserPushAllowed = async (name, user) => { + name = name.toLowerCase(); + return new Promise(async (resolve, reject) => { + const repo = await exports.getRepo(name); + console.log(repo.users.canPush); + console.log(repo.users.canAuthorise); + + if (repo.users.canPush.includes(user) || repo.users.canAuthorise.includes(user)) { + resolve(true); + } else { + resolve(false); + } + }); +}; + +exports.canUserApproveRejectPushRepo = async (name, user) => { + name = name.toLowerCase(); + console.log(`checking if user ${user} can approve/reject for ${name}`); + return new Promise(async (resolve, reject) => { + const repo = await exports.getRepo(name); + if (repo.users.canAuthorise.includes(user)) { + console.log(`user ${user} can approve/reject to repo ${name}`); + resolve(true); + } else { + console.log(`user ${user} cannot approve/reject to repo ${name}`); + resolve(false); + } + }); +}; diff --git a/src/db/index.js b/src/db/index.js index d633f057..2751f071 100644 --- a/src/db/index.js +++ b/src/db/index.js @@ -1,25 +1,13 @@ -const nodemailer = require('nodemailer'); -const generator = require('generate-password'); -const passwordHash = require('password-hash'); -const validator = require('email-validator'); - +const bcrypt = require('bcrypt'); const config = require('../config'); - -if (config.getDatabase().type === 'fs') { - sink = require('../db/file'); -} - +let sink; if (config.getDatabase().type === 'mongo') { sink = require('../db/mongo'); +} else if (config.getDatabase().type === 'fs') { + sink = require('../db/file'); } -module.exports.createUser = async ( - username, - password, - email, - gitAccount, - admin = false, -) => { +module.exports.createUser = async (username, password, email, gitAccount, admin = false) => { console.log( `creating user user=${username}, @@ -30,14 +18,26 @@ module.exports.createUser = async ( const data = { username: username, - password: passwordHash.generate(password), + password: await bcrypt.hash(password, 10), gitAccount: gitAccount, email: email, admin: admin, - changePassword: true, - token: generator.generate({ length: 10, numbers: true }), }; + if (username === undefined || username === null || username === '') { + const errorMessage = `username ${username} cannot be empty`; + throw new Error(errorMessage); + } + + if (gitAccount === undefined || gitAccount === null || gitAccount === '') { + const errorMessage = `GitAccount ${gitAccount} cannot be empty`; + throw new Error(errorMessage); + } + + if (email === undefined || email === null || email === '') { + const errorMessage = `Email ${email} cannot be empty`; + throw new Error(errorMessage); + } const existingUser = await sink.findUser(username); if (existingUser) { @@ -45,44 +45,7 @@ module.exports.createUser = async ( throw new Error(errorMessage); } - sink.createUser(data); - - await wrapedSendMail(data, password); -}; - -const wrapedSendMail = function (data, password) { - return new Promise((resolve, reject) => { - const emailConfig = config.getTempPasswordConfig(); - - if (!emailConfig.sendEmail) { - resolve(); - return; - } - - if (!validator.validate(data.email)) { - resolve(); - return; - } - - const transporter = nodemailer.createTransport(emailConfig.emailConfig); - - const mailOptions = { - from: emailConfig.from, - to: data.email, - subject: 'Git Proxy - temporary password', - text: `Your tempoary password is ${password}`, - }; - - transporter.sendMail(mailOptions, function (error, info) { - if (error) { - console.log(`error is ${error}`); - reject(error); - } else { - console.log('Email sent: ' + info.response); - resolve(true); - } - }); - }); + await sink.createUser(data); }; // The module exports @@ -105,3 +68,8 @@ module.exports.removeUserCanAuthorise = sink.removeUserCanAuthorise; module.exports.removeUserCanPush = sink.removeUserCanPush; module.exports.deleteRepo = sink.deleteRepo; +module.exports.isUserPushAllowed = sink.isUserPushAllowed; +module.exports.canUserApproveRejectPushRepo = sink.canUserApproveRejectPushRepo; +module.exports.canUserApproveRejectPush = sink.canUserApproveRejectPush; +module.exports.canUserCancelPush = sink.canUserCancelPush; +module.exports.getSessionStore = sink.getSessionStore; diff --git a/src/db/mongo/helper.js b/src/db/mongo/helper.js index 128d79df..116b35c0 100644 --- a/src/db/mongo/helper.js +++ b/src/db/mongo/helper.js @@ -3,19 +3,24 @@ const config = require('../../config'); const dbConfig = config.getDatabase(); const options = dbConfig.options; const connectionString = dbConfig.connectionString; -// let client; -// let db; +const MongoDBStore = require('connect-mongo'); + +let _db; -// use client to work with db exports.connect = async (collectionName) => { - try { + if (!_db) { const client = new mongo.MongoClient(connectionString, options); await client.connect(); - const db = await client.db(); - - const collection = db.collection(collectionName); - return collection; - } catch (err) { - throw err; + _db = await client.db(); } + + return _db.collection(collectionName); +}; + +exports.getSessionStore = (session) => { + return new MongoDBStore({ + mongoUrl: connectionString, + collectionName: 'user_session', + mongoOptions: options, + }); }; diff --git a/src/db/mongo/index.js b/src/db/mongo/index.js index 583be503..d8687f30 100644 --- a/src/db/mongo/index.js +++ b/src/db/mongo/index.js @@ -1,6 +1,7 @@ const pushes = require('./pushes'); const users = require('./users'); const repo = require('./repo'); +const helper = require('./helper'); module.exports.getPushes = pushes.getPushes; module.exports.writeAudit = pushes.writeAudit; @@ -8,6 +9,9 @@ module.exports.getPush = pushes.getPush; module.exports.authorise = pushes.authorise; module.exports.cancel = pushes.cancel; module.exports.reject = pushes.reject; +module.exports.canUserApproveRejectPush = pushes.canUserApproveRejectPush; +module.exports.canUserCancelPush = pushes.canUserCancelPush; + module.exports.findUser = users.findUser; module.exports.getUsers = users.getUsers; module.exports.createUser = users.createUser; @@ -21,5 +25,8 @@ module.exports.addUserCanPush = repo.addUserCanPush; module.exports.addUserCanAuthorise = repo.addUserCanAuthorise; module.exports.removeUserCanPush = repo.removeUserCanPush; module.exports.removeUserCanAuthorise = repo.removeUserCanAuthorise; - module.exports.deleteRepo = repo.deleteRepo; +module.exports.isUserPushAllowed = repo.isUserPushAllowed; +module.exports.canUserApproveRejectPushRepo = repo.canUserApproveRejectPushRepo; + +module.exports.getSessionStore = helper.getSessionStore; diff --git a/src/db/mongo/pushes.js b/src/db/mongo/pushes.js index 1203a6a7..0927a6db 100644 --- a/src/db/mongo/pushes.js +++ b/src/db/mongo/pushes.js @@ -1,6 +1,7 @@ const connect = require('./helper').connect; const Action = require('../../proxy/actions').Action; const toClass = require('../helper').toClass; +const repo = require('./repo'); const cnName = 'pushes'; const defaultPushQuery = { @@ -12,8 +13,32 @@ const defaultPushQuery = { const getPushes = async (query = defaultPushQuery) => { const collection = await connect(cnName); - const results = await collection.find(query).toArray(); - return results; + return collection + .find(query, { + projection: { + _id: 0, + id: 1, + allowPush: 1, + authorised: 1, + blocked: 1, + blockedMessage: 1, + branch: 1, + canceled: 1, + commitData: 1, + commitFrom: 1, + commitTo: 1, + error: 1, + method: 1, + project: 1, + rejected: 1, + repo: 1, + repoName: 1, + timepstamp: 1, + type: 1, + url: 1, + }, + }) + .toArray(); }; const getPush = async (id) => { @@ -36,11 +61,12 @@ const writeAudit = async (action) => { return action; }; -const authorise = async (id) => { +const authorise = async (id, attestation) => { const action = await getPush(id); action.authorised = true; action.canceled = false; action.rejected = false; + action.attestation = attestation; await writeAudit(action); return { message: `authorised ${id}` }; }; @@ -60,7 +86,31 @@ const cancel = async (id) => { action.canceled = true; action.rejected = false; await writeAudit(action); - return { message: `cancel ${id}` }; + return { message: `canceled ${id}` }; +}; + +const canUserApproveRejectPush = async (id, user) => { + return new Promise(async (resolve) => { + const action = await getPush(id); + const repoName = action.repoName.replace('.git', ''); + const isAllowed = await repo.canUserApproveRejectPushRepo(repoName, user); + + resolve(isAllowed); + }); +}; + +const canUserCancelPush = async (id, user) => { + return new Promise(async (resolve) => { + const pushDetail = await getPush(id); + const repoName = pushDetail.repoName.replace('.git', ''); + const isAllowed = await repo.isUserPushAllowed(repoName, user); + + if (isAllowed) { + resolve(true); + } else { + resolve(false); + } + }); }; module.exports.getPushes = getPushes; @@ -69,3 +119,5 @@ module.exports.getPush = getPush; module.exports.authorise = authorise; module.exports.reject = reject; module.exports.cancel = cancel; +module.exports.canUserApproveRejectPush = canUserApproveRejectPush; +module.exports.canUserCancelPush = canUserCancelPush; diff --git a/src/db/mongo/repo.js b/src/db/mongo/repo.js index 698142f7..1c604069 100644 --- a/src/db/mongo/repo.js +++ b/src/db/mongo/repo.js @@ -1,19 +1,33 @@ const connect = require('./helper').connect; const cnName = 'repos'; +const isBlank = (str) => { + return !str || /^\s*$/.test(str); +}; + exports.getRepos = async (query = {}) => { const collection = await connect(cnName); - const result = await collection.find().toArray(); - console.log(JSON.stringify(result)); - return result; + return collection.find().toArray(); }; exports.getRepo = async (name) => { const collection = await connect(cnName); - return await collection.findOne({ name: name }); + return collection.findOne({ name: { $eq: name } }); }; exports.createRepo = async (repo) => { + console.log(`creating new repo ${JSON.stringify(repo)}`); + + if (isBlank(repo.project)) { + throw new Error('Project name cannot be empty'); + } + if (isBlank(repo.name)) { + throw new Error('Repository name cannot be empty'); + } + if (isBlank(repo.url)) { + throw new Error('URL cannot be empty'); + } + repo.users = { canPush: [], canAuthorise: [], @@ -21,41 +35,64 @@ exports.createRepo = async (repo) => { const collection = await connect(cnName); await collection.insertOne(repo); + console.log(`created new repo ${JSON.stringify(repo)}`); }; exports.addUserCanPush = async (name, user) => { + name = name.toLowerCase(); const collection = await connect(cnName); - await collection.updateOne( - { name: name }, - { $push: { 'users.canPush': user } }, - ); + await collection.updateOne({ name: name }, { $push: { 'users.canPush': user } }); }; exports.addUserCanAuthorise = async (name, user) => { + name = name.toLowerCase(); const collection = await connect(cnName); - await collection.updateOne( - { name: name }, - { $push: { 'users.canAuthorise': user } }, - ); + await collection.updateOne({ name: name }, { $push: { 'users.canAuthorise': user } }); }; exports.removeUserCanPush = async (name, user) => { + name = name.toLowerCase(); const collection = await connect(cnName); - await collection.updateOne( - { name: name }, - { $pull: { 'users.canPush': user } }, - ); + await collection.updateOne({ name: name }, { $pull: { 'users.canPush': user } }); }; exports.removeUserCanAuthorise = async (name, user) => { + name = name.toLowerCase(); const collection = await connect(cnName); - await collection.updateOne( - { name: name }, - { $pull: { 'users.canAuthorise': user } }, - ); + await collection.updateOne({ name: name }, { $pull: { 'users.canAuthorise': user } }); }; exports.deleteRepo = async (name) => { const collection = await connect(cnName); await collection.deleteMany({ name: name }); }; + +exports.isUserPushAllowed = async (name, user) => { + name = name.toLowerCase(); + return new Promise(async (resolve, reject) => { + const repo = await exports.getRepo(name); + console.log(repo.users.canPush); + console.log(repo.users.canAuthorise); + + if (repo.users.canPush.includes(user) || repo.users.canAuthorise.includes(user)) { + resolve(true); + } else { + resolve(false); + } + }); +}; + +exports.canUserApproveRejectPushRepo = async (name, user) => { + name = name.toLowerCase(); + console.log(`checking if user ${user} can approve/reject for ${name}`); + return new Promise(async (resolve, reject) => { + const repo = await exports.getRepo(name); + if (repo.users.canAuthorise.includes(user)) { + console.log(`user ${user} can approve/reject to repo ${name}`); + resolve(true); + } else { + console.log(`user ${user} cannot approve/reject to repo ${name}`); + resolve(false); + } + }); +}; diff --git a/src/db/mongo/users.js b/src/db/mongo/users.js index 90d7edc4..46ee250a 100644 --- a/src/db/mongo/users.js +++ b/src/db/mongo/users.js @@ -1,35 +1,31 @@ -/* eslint-disable max-len */ const connect = require('./helper').connect; const usersCollection = 'users'; exports.findUser = async function (username) { const collection = await connect(usersCollection); - return await collection.findOne({ username: username }); + return collection.findOne({ username: { $eq: username } }); }; exports.getUsers = async function (query) { + console.log(`Getting users for query= ${JSON.stringify(query)}`); const collection = await connect(usersCollection); - return await collection.find().toArray(); + return collection.find(query).toArray(); }; exports.deleteUser = async function (username) { const collection = await connect(usersCollection); - return await collection.deleteOne({ username: username }); + return collection.deleteOne({ username: username }); }; exports.createUser = async function (data) { - console.log(JSON.stringify(data)); + data.username = data.username.toLowerCase(); const collection = await connect(usersCollection); - const result = await collection.insertOne(data); - return result; + return collection.insertOne(data); }; exports.updateUser = async (user) => { + user.username = user.username.toLowerCase(); const options = { upsert: true }; const collection = await connect(usersCollection); - await collection.updateOne( - { username: user.username }, - { $set: user }, - options, - ); + await collection.updateOne({ username: user.username }, { $set: user }, options); }; diff --git a/src/index.jsx b/src/index.jsx index b9965589..4aca4983 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -1,18 +1,11 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import ReactDOM from 'react-dom'; import { createBrowserHistory } from 'history'; -import { - BrowserRouter as Router, - Route, - Routes, - Navigate, -} from 'react-router-dom'; +import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom'; // core components -import Admin from './ui/layouts/Admin.jsx'; -import Login from './ui/views/Login/Login.jsx'; +import Admin from './ui/layouts/Admin'; +import Login from './ui/views/Login/Login'; import './ui/assets/css/material-dashboard-react.css'; const hist = createBrowserHistory(); @@ -20,9 +13,9 @@ const hist = createBrowserHistory(); ReactDOM.render( - } /> - } /> - } /> + } /> + } /> + } /> , document.getElementById('root'), diff --git a/src/logo.svg b/src/logo.svg deleted file mode 100644 index 6b60c104..00000000 --- a/src/logo.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/proxy/actions/Action.js b/src/proxy/actions/Action.js index a6419af6..50b0e8fa 100644 --- a/src/proxy/actions/Action.js +++ b/src/proxy/actions/Action.js @@ -1,22 +1,5 @@ /** Class representing a Push. */ - -/** - * - * @param {string} str1 - * @param {string} str2 - * @param {boolean} ignore - * @return {string} - */ -// eslint-disable-next-line no-extend-native -String.prototype.replaceAll = function (str1, str2, ignore) { - return this.replace( - new RegExp( - str1.replace(/([\/\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|\<\>\-\&])/g, '\\$&'), - ignore ? 'gi' : 'g', - ), - typeof str2 == 'string' ? str2.replace(/\$/g, '$$$$') : str2, - ); -}; +const config = require('../../config'); /** * Create a new action @@ -37,6 +20,7 @@ class Action { message; author; user; + attestation; /** * @@ -53,7 +37,7 @@ class Action { this.timestamp = timestamp; this.project = repo.split('/')[0]; this.repoName = repo.split('/')[1]; - this.url = `https://github.com/${repo}`; + this.url = `${config.getProxyUrl()}/${repo}`; this.repo = repo; } @@ -112,30 +96,6 @@ class Action { this.message = message; } - /** - * - * @param {*} branch - */ - setBranch(branch) { - this.branch = branch; - } - - /** - * - * @param {*} branch - */ - setAuthor(branch) { - this.author = author; - } - - /** - * - * @param {*} branch - */ - setUser(branch) { - this.user = user; - } - /** *` */ @@ -148,8 +108,7 @@ class Action { * @return {bool} */ continue() { - const cont = !(this.error || this.blocked); - return cont; + return !(this.error || this.blocked); } } diff --git a/src/proxy/chain.js b/src/proxy/chain.js index 39a67d7f..68982eef 100644 --- a/src/proxy/chain.js +++ b/src/proxy/chain.js @@ -4,10 +4,14 @@ const plugin = require('../plugin'); const pushActionChain = [ proc.push.parsePush, proc.push.checkRepoInAuthorisedList, + proc.push.checkCommitMessages, + proc.push.checkAuthorEmails, + proc.push.checkUserPushPermission, proc.push.checkIfWaitingAuth, proc.push.pullRemote, proc.push.writePack, proc.push.getDiff, + proc.push.scanDiff, proc.push.blockForAuth, ]; @@ -17,15 +21,12 @@ const chain = async (req) => { let action; try { action = await proc.pre.parseAction(req); - const actions = await getChain(action); - for (const i in actions) { if (!i) continue; const fn = actions[i]; action = await fn(req, action); - if (!action.continue()) { return action; } @@ -34,8 +35,6 @@ const chain = async (req) => { return action; } } - } catch (e) { - throw e; } finally { await proc.push.audit(req, action); } @@ -44,7 +43,7 @@ const chain = async (req) => { }; const getChain = async (action) => { - if (action.type === 'pull') return []; + if (action.type === 'pull') return [proc.push.checkRepoInAuthorisedList]; if (action.type === 'push') { // insert loaded plugins as actions // this probably isn't the place to insert these functions @@ -63,6 +62,7 @@ const getChain = async (action) => { } return pushActionChain; } + if (action.type === 'default') return []; }; exports.exec = chain; diff --git a/src/proxy/index.js b/src/proxy/index.js index 6943bc77..4dfc14d6 100644 --- a/src/proxy/index.js +++ b/src/proxy/index.js @@ -1,4 +1,3 @@ -/* eslint-disable max-len */ const proxyApp = require('express')(); const bodyParser = require('body-parser'); const router = require('./routes').router; @@ -22,9 +21,7 @@ const start = async () => { const allowedList = await db.getRepos(); defaultAuthorisedRepoList.forEach(async (x) => { - const found = allowedList.find( - (y) => y.project == x.project && x.name == y.name, - ); + const found = allowedList.find((y) => y.project === x.project && x.name === y.name); if (!found) { await db.createRepo(x); await db.addUserCanPush(x.name, 'admin'); @@ -33,9 +30,8 @@ const start = async () => { }); proxyApp.listen(proxyHttpPort, () => { - console.log(`Listening on ${proxyHttpPort}`); + console.log(`Proxy Listening on ${proxyHttpPort}`); }); - return proxyApp; }; diff --git a/src/proxy/processors/pre-processor/parseAction.js b/src/proxy/processors/pre-processor/parseAction.js index a6fd3b5b..b10f0372 100644 --- a/src/proxy/processors/pre-processor/parseAction.js +++ b/src/proxy/processors/pre-processor/parseAction.js @@ -6,7 +6,11 @@ const exec = async (req) => { const repoName = getRepoNameFromUrl(req.originalUrl); const paths = req.originalUrl.split('/'); - let type = 'pull'; + let type = 'default'; + + if (paths[paths.length - 1].endsWith('git-upload-pack') && req.method == 'GET') { + type = 'pull'; + } if ( paths[paths.length - 1] == 'git-receive-pack' && req.method == 'POST' && @@ -14,13 +18,11 @@ const exec = async (req) => { ) { type = 'push'; } - return new actions.Action(id, type, req.method, timestamp, repoName); }; const getRepoNameFromUrl = (url) => { const parts = url.split('/'); - for (let i = 0, len = parts.length; i < len; i++) { const part = parts[i]; if (part.endsWith('.git')) { diff --git a/src/proxy/processors/push-action/blockForAuth.js b/src/proxy/processors/push-action/blockForAuth.js index 79356b47..20acb603 100644 --- a/src/proxy/processors/push-action/blockForAuth.js +++ b/src/proxy/processors/push-action/blockForAuth.js @@ -7,10 +7,12 @@ const exec = async (req, action) => { const message = '\n\n\n' + - `Git Proxy has received your push:\n\n` + - `http://localhost:${uiPort}/requests/${action.id}` + + `\x1B[32mGit Proxy has received your push ✅\x1B[0m\n\n` + + '🔗 Shareable Link\n\n' + + `\x1B[34mhttp://localhost:${uiPort}/admin/push/${action.id}\x1B[0m` + '\n\n\n'; step.setAsyncBlock(message); + action.addStep(step); return action; }; diff --git a/src/proxy/processors/push-action/checkAuthorEmails.js b/src/proxy/processors/push-action/checkAuthorEmails.js new file mode 100644 index 00000000..70c973d0 --- /dev/null +++ b/src/proxy/processors/push-action/checkAuthorEmails.js @@ -0,0 +1,65 @@ +const Step = require('../../actions').Step; +const config = require('../../../config'); + +const commitConfig = config.getCommitConfig(); + +function isEmailAllowed(email) { + const [emailLocal, emailDomain] = email.split('@'); + console.log({ emailLocal, emailDomain }); + + // E-mail address is not a permissible domain name + if ( + commitConfig.author.email.domain.allow && + !emailDomain.match(new RegExp(commitConfig.author.email.domain.allow, 'g')) + ) { + console.log('Bad e-mail address domain...'); + return false; + } + + // E-mail username is not a permissible form + if ( + commitConfig.author.email.local.block && + emailLocal.match(new RegExp(commitConfig.author.email.local.block, 'g')) + ) { + console.log('Bad e-mail address username...'); + return false; + } + + return true; +} + +// Execute if the repo is approved +const exec = async (req, action) => { + console.log({ req, action }); + + const step = new Step('checkAuthorEmails'); + + const uniqueAuthorEmails = [...new Set(action.commitData.map((commit) => commit.authorEmail))]; + console.log({ uniqueAuthorEmails }); + + const illegalEmails = uniqueAuthorEmails.filter((email) => !isEmailAllowed(email)); + console.log({ illegalEmails }); + + const usingIllegalEmails = illegalEmails.length > 0; + console.log({ usingIllegalEmails }); + + if (usingIllegalEmails) { + console.log(`The following commit author e-mails are illegal: ${illegalEmails}`); + + step.error = true; + step.log(`The following commit author e-mails are illegal: ${illegalEmails}`); + step.setError( + 'Your push has been blocked. Please verify your Git configured e-mail address is valid (e.g. john.smith@example.com)', + ); + + action.addStep(step); + return action; + } + + console.log(`The following commit author e-mails are legal: ${uniqueAuthorEmails}`); + action.addStep(step); + return action; +}; + +exec.displayName = 'checkAuthorEmails.exec'; +exports.exec = exec; diff --git a/src/proxy/processors/push-action/checkCommitMessages.js b/src/proxy/processors/push-action/checkCommitMessages.js new file mode 100644 index 00000000..8f65933c --- /dev/null +++ b/src/proxy/processors/push-action/checkCommitMessages.js @@ -0,0 +1,86 @@ +const Step = require('../../actions').Step; +const config = require('../../../config'); + +const commitConfig = config.getCommitConfig(); + +function isMessageAllowed(commitMessage) { + console.log(`isMessageAllowed(${commitMessage})`); + + // Commit message is empty, i.e. '', null or undefined + if (!commitMessage) { + console.log('No commit message included...'); + return false; + } + + // Validation for configured block pattern(s) check... + if (typeof commitMessage !== 'string') { + console.log('A non-string value has been captured for the commit message...'); + return false; + } + + // Configured blocked literals + const blockedLiterals = commitConfig.message.block.literals; + + // Configured blocked patterns + const blockedPatterns = commitConfig.message.block.patterns; + + // Find all instances of blocked literals in commit message... + const positiveLiterals = blockedLiterals.map((literal) => + commitMessage.toLowerCase().includes(literal.toLowerCase()), + ); + + // Find all instances of blocked patterns in commit message... + const positivePatterns = blockedPatterns.map((pattern) => + commitMessage.match(new RegExp(pattern, 'gi')), + ); + + // Flatten any positive literal results into a 1D array... + const literalMatches = positiveLiterals.flat().filter((result) => !!result); + + // Flatten any positive pattern results into a 1D array... + const patternMatches = positivePatterns.flat().filter((result) => !!result); + + // Commit message matches configured block pattern(s) + if (literalMatches.length || patternMatches.length) { + console.log('Commit message is blocked via configured literals/patterns...'); + return false; + } + + return true; +} + +// Execute if the repo is approved +const exec = async (req, action) => { + console.log({ req, action }); + + const step = new Step('checkCommitMessages'); + + const uniqueCommitMessages = [...new Set(action.commitData.map((commit) => commit.message))]; + console.log({ uniqueCommitMessages }); + + const illegalMessages = uniqueCommitMessages.filter((message) => !isMessageAllowed(message)); + console.log({ illegalMessages }); + + const usingIllegalMessages = illegalMessages.length > 0; + console.log({ usingIllegalMessages }); + + if (usingIllegalMessages) { + console.log(`The following commit messages are illegal: ${illegalMessages}`); + + step.error = true; + step.log(`The following commit messages are illegal: ${illegalMessages}`); + step.setError( + '\n\n\n\nYour push has been blocked.\nPlease ensure your commit message(s) does not contain sensitive information or URLs.\n\n\n', + ); + + action.addStep(step); + return action; + } + + console.log(`The following commit messages are legal: ${uniqueCommitMessages}`); + action.addStep(step); + return action; +}; + +exec.displayName = 'checkCommitMessages.exec'; +exports.exec = exec; diff --git a/src/proxy/processors/push-action/checkIfWaitingAuth.js b/src/proxy/processors/push-action/checkIfWaitingAuth.js index f50a3bbf..f671cd08 100644 --- a/src/proxy/processors/push-action/checkIfWaitingAuth.js +++ b/src/proxy/processors/push-action/checkIfWaitingAuth.js @@ -19,8 +19,8 @@ const exec = async (req, action) => { throw e; } finally { action.addStep(step); - return action; } + return action; }; exec.displayName = 'checkIfWaitingAuth.exec'; diff --git a/src/proxy/processors/push-action/checkRepoInAuthorisedList.js b/src/proxy/processors/push-action/checkRepoInAuthorisedList.js index f11216ba..e43a1d20 100644 --- a/src/proxy/processors/push-action/checkRepoInAuthorisedList.js +++ b/src/proxy/processors/push-action/checkRepoInAuthorisedList.js @@ -9,8 +9,8 @@ const exec = async (req, action, authorisedList = db.getRepos) => { console.log(list); const found = list.find((x) => { - const targetName = action.repo.replace('.git', ''); - const allowedName = `${x.project}/${x.name}`.replace('.git', ''); + const targetName = action.repo.replace('.git', '').toLowerCase(); + const allowedName = `${x.project}/${x.name}`.replace('.git', '').toLowerCase(); console.log(`${targetName} = ${allowedName}`); return targetName === allowedName; }); diff --git a/src/proxy/processors/push-action/checkUserPushPermission.js b/src/proxy/processors/push-action/checkUserPushPermission.js new file mode 100644 index 00000000..3daff825 --- /dev/null +++ b/src/proxy/processors/push-action/checkUserPushPermission.js @@ -0,0 +1,46 @@ +const Step = require('../../actions').Step; +const db = require('../../../db'); + +// Execute if the repo is approved +const exec = async (req, action) => { + const step = new Step('checkUserPushPermission'); + + const repoName = action.repo.split('/')[1].replace('.git', ''); + let isUserAllowed = false; + let user = action.user; + + // Find the user associated with this Git Account + const list = await db.getUsers({ gitAccount: action.user }); + + console.log(JSON.stringify(list)); + + if (list.length == 1) { + user = list[0].username; + isUserAllowed = await db.isUserPushAllowed(repoName, user); + } + + console.log(`User ${user} permission on Repo ${repoName} : ${isUserAllowed}`); + + if (!isUserAllowed) { + console.log('User not allowed to Push'); + step.error = true; + step.log(`User ${user} is not allowed to push on repo ${action.repo}, ending`); + + console.log('setting error'); + + step.setError( + `Rejecting push as user ${action.user} ` + + `is not allowed to push on repo ` + + `${action.repo}`, + ); + action.addStep(step); + return action; + } + + step.log(`User ${user} is allowed to push on repo ${action.repo}`); + action.addStep(step); + return action; +}; + +exec.displayName = 'checkUserPushPermission.exec'; +exports.exec = exec; diff --git a/src/proxy/processors/push-action/getDiff.js b/src/proxy/processors/push-action/getDiff.js index 5ed15691..4344810b 100644 --- a/src/proxy/processors/push-action/getDiff.js +++ b/src/proxy/processors/push-action/getDiff.js @@ -1,4 +1,3 @@ -/* eslint-disable max-len */ const child = require('child_process'); const Step = require('../../actions').Step; @@ -7,42 +6,35 @@ const exec = async (req, action) => { try { const path = `${action.proxyGitPath}/${action.repoName}`; - // If this is a new repo or fresh branch - // see https://stackoverflow.com/questions/40883798/how-to-get-git-diff-of-the-first-commit - let cmd = `git diff 4b825dc642cb6eb9a060e54bf8d69288fbee4904 ${action.commitTo}`; + + // https://stackoverflow.com/questions/40883798/how-to-get-git-diff-of-the-first-commit + let commitFrom = `4b825dc642cb6eb9a060e54bf8d69288fbee4904`; if (action.commitFrom === '0000000000000000000000000000000000000000') { - if ( - action.commitData[0].parent !== - '0000000000000000000000000000000000000000' - ) { - cmd = `git diff ${ - action.commitData[action.commitData.length - 1].parent - } ${action.commitTo}`; + if (action.commitData[0].parent !== '0000000000000000000000000000000000000000') { + commitFrom = `${action.commitData[action.commitData.length - 1].parent}`; } } else { - cmd = `git diff ${action.commitFrom} ${action.commitTo}`; + commitFrom = `${action.commitFrom}`; } - step.log(`executing "${cmd}" in foler ${path}`); + step.log(`Executing "git diff ${commitFrom} ${action.commitTo}" in ${path}`); // Get the diff - const content = child - .execSync(cmd, { - cwd: path, - encoding: 'utf8', - maxBuffer: 50 * 1024 * 1024, - }) - .toString('utf-8'); - - step.log(`completed ${cmd}`); + const content = child.spawnSync('git', ['diff', commitFrom, action.commitTo], { + cwd: path, + encoding: 'utf-8', + maxBuffer: 50 * 1024 * 1024, + }).stdout; + + step.log(content); step.setContent(content); } catch (e) { step.setError(e.toString('utf-8')); } finally { action.addStep(step); - return action; } + return action; }; exec.displayName = 'getDiff.exec'; diff --git a/src/proxy/processors/push-action/index.js b/src/proxy/processors/push-action/index.js index 97eed1f7..309b82cd 100644 --- a/src/proxy/processors/push-action/index.js +++ b/src/proxy/processors/push-action/index.js @@ -4,5 +4,9 @@ exports.audit = require('./audit').exec; exports.pullRemote = require('./pullRemote').exec; exports.writePack = require('./writePack').exec; exports.getDiff = require('./getDiff').exec; +exports.scanDiff = require('./scanDiff').exec; exports.blockForAuth = require('./blockForAuth').exec; exports.checkIfWaitingAuth = require('./checkIfWaitingAuth').exec; +exports.checkCommitMessages = require('./checkCommitMessages').exec; +exports.checkAuthorEmails = require('./checkAuthorEmails').exec; +exports.checkUserPushPermission = require('./checkUserPushPermission').exec; diff --git a/src/proxy/processors/push-action/parsePush.js b/src/proxy/processors/push-action/parsePush.js index 0a04c33a..aff5cf72 100644 --- a/src/proxy/processors/push-action/parsePush.js +++ b/src/proxy/processors/push-action/parsePush.js @@ -11,6 +11,7 @@ if (!fs.existsSync(dir)) { const exec = async (req, action) => { const step = new Step('parsePackFile'); + try { const messageParts = req.rawBody.split(' '); action.branch = messageParts[2].trim().replace('\u0000', ''); @@ -24,41 +25,96 @@ const exec = async (req, action) => { action.commitData = getCommitData(contents); if (action.commitFrom === '0000000000000000000000000000000000000000') { - action.commitFrom = - action.commitData[action.commitData.length - 1].parent; + action.commitFrom = action.commitData[action.commitData.length - 1].parent; } + const user = action.commitData[action.commitData.length - 1].committer; + console.log(`Push Request received from user ${user}`); + action.user = user; + step.content = { meta: meta, }; } catch (e) { - step.setError(e.toString('utf-8')); - throw e; + step.setError( + `Unable to parse push. Please contact an administrator for support: ${e.toString('utf-8')}`, + ); } finally { action.addStep(step); - return action; } + return action; }; const getCommitData = (contents) => { + console.log({ contents }); return lod .chain(contents) .filter({ type: 1 }) .map((x) => { - const parts = x.content.split('\n'); - const tree = parts[0]; - const parent = parts[1]; - const author = parts[2]; - const committer = parts[3]; - const message = parts[5]; + console.log({ x }); + + const formattedContent = x.content.split('\n'); + console.log({ formattedContent }); + + const parts = formattedContent.filter((part) => part.length > 0); + console.log({ parts }); + + const tree = parts + .find((t) => t.split(' ')[0] === 'tree') + .replace('tree', '') + .trim(); + console.log({ tree }); + + const parent = parts + .find((t) => t.split(' ')[0] === 'parent') + .replace('parent', '') + .trim(); + console.log({ parent }); + + const author = parts + .find((t) => t.split(' ')[0] === 'author') + .replace('author', '') + .trim(); + console.log({ author }); + + const committer = parts + .find((t) => t.split(' ')[0] === 'committer') + .replace('committer', '') + .trim(); + console.log({ committer }); + + const indexOfMessages = formattedContent.indexOf(''); + console.log({ indexOfMessages }); + + const message = formattedContent + .slice(indexOfMessages + 1, formattedContent.length - 1) + .join(' '); + console.log({ message }); + + const commitTimestamp = committer.split(' ').reverse()[1]; + console.log({ commitTimestamp }); + + const authorEmail = author.split(' ').reverse()[2].slice(1, -1); + console.log({ authorEmail }); + + console.log({ + tree, + parent, + author: author.split('<')[0].trim(), + committer: committer.split('<')[0].trim(), + commitTimestamp, + message, + authorEmail, + }); return { - tree: tree.replace('parent', '').trim(), - parent: parent.replace('parent', '').trim(), - author: author.split(' ')[1], - committer: committer.split(' ')[1], - commitTs: author.split(' ')[3], - message: message, + tree, + parent, + author: author.split('<')[0].trim(), + committer: committer.split('<')[0].trim(), + commitTimestamp, + message, + authorEmail, }; }) .value(); @@ -86,7 +142,9 @@ const getContents = (buffer, entries) => { const [content, nextBuffer] = getContent(i, buffer); buffer = nextBuffer; contents.push(content); - } catch (e) {} + } catch (e) { + console.log(e); + } } return contents; }; @@ -95,7 +153,7 @@ const getInt = (bits) => { let strBits = ''; // eslint-disable-next-line guard-for-in - for (i in bits) { + for (const i in bits) { strBits += bits[i] ? 1 : 0; } @@ -121,21 +179,21 @@ const getContent = (item, buffer) => { // 8 bytes while (more) { buffer = buffer.slice(1); - const byte = buffer.readUIntBE(0, 1); - const m = new BitMask(byte); + const nextByte = buffer.readUIntBE(0, 1); + const nextM = new BitMask(nextByte); const nextSize = [ - m.getBit(4), - m.getBit(5), - m.getBit(6), - m.getBit(7), - m.getBit(8), - m.getBit(9), - m.getBit(10), + nextM.getBit(4), + nextM.getBit(5), + nextM.getBit(6), + nextM.getBit(7), + nextM.getBit(8), + nextM.getBit(9), + nextM.getBit(10), ]; size = nextSize.concat(size); - more = m.getBit(3); + more = nextM.getBit(3); } // NOTE Size is the unziped size, not the zipped size @@ -147,7 +205,7 @@ const getContent = (item, buffer) => { buffer = buffer.slice(20); } - contentBuffer = buffer.slice(1); + const contentBuffer = buffer.slice(1); const [content, deflatedSize] = unpack(contentBuffer); // NOTE Size is the unziped size, not the zipped size @@ -163,7 +221,7 @@ const getContent = (item, buffer) => { }; // Move on by the zipped content size. - nextBuffer = contentBuffer.slice(deflatedSize); + const nextBuffer = contentBuffer.slice(deflatedSize); return [result, nextBuffer]; }; diff --git a/src/proxy/processors/push-action/pullRemote.js b/src/proxy/processors/push-action/pullRemote.js index ecf1c6fb..a851044e 100644 --- a/src/proxy/processors/push-action/pullRemote.js +++ b/src/proxy/processors/push-action/pullRemote.js @@ -1,4 +1,4 @@ -const execSync = require('child_process').execSync; +const spawnSync = require('child_process').spawnSync; const Step = require('../../actions').Step; const fs = require('fs'); const dir = './.remote'; @@ -20,22 +20,43 @@ const exec = async (req, action) => { } const cmd = `git clone ${action.url} --bare`; - step.log(`Exectuting ${cmd}`); - // eslint-disable-next-line max-len - const response = execSync(`git clone ${action.url} --bare`, { + // Retrieve authorization headers + const authorizationHeader = req.headers?.authorization; + + // Validate the authorization headers + const authorizationValid = + authorizationHeader && + typeof authorizationHeader === 'string' && + authorizationHeader.includes('Basic '); + + // Construct clone URL depending on presence of authorization headers + const cloneUrl = authorizationValid + ? `https://${Buffer.from(authorizationHeader.split(' ')[1], 'base64')}@${action.url.replace( + /https*:\/\//, + '', + )}` + : action.url; + + step.log(`Exectuting ${cmd}${authorizationValid ? ' with credentials' : ''}`); + + const response = spawnSync('git', ['clone', cloneUrl, '--bare', '--progress'], { cwd: action.proxyGitPath, - }).toString('utf-8'); + encoding: 'utf-8', + }); + + const cloneOutput = response?.stderr; + step.log(cloneOutput); step.log(`Completed ${cmd}`); - step.setContent(response); + step.setContent(cloneOutput); } catch (e) { step.setError(e.toString('utf-8')); throw e; } finally { action.addStep(step); - return action; } + return action; }; exec.displayName = 'pullRemote.exec'; diff --git a/src/proxy/processors/push-action/scanDiff.js b/src/proxy/processors/push-action/scanDiff.js new file mode 100644 index 00000000..3fcce9d5 --- /dev/null +++ b/src/proxy/processors/push-action/scanDiff.js @@ -0,0 +1,99 @@ +const Step = require('../../actions').Step; +const config = require('../../../config'); + +const commitConfig = config.getCommitConfig(); +const privateOrganizations = config.getPrivateOrganizations(); + +const isDiffLegal = (diff, organization) => { + // Commit diff is empty, i.e. '', null or undefined + if (!diff) { + console.log('No commit diff...'); + return false; + } + + // Validation for configured block pattern(s) check... + if (typeof diff !== 'string') { + console.log('A non-string value has been captured for the commit diff...'); + return false; + } + + // Configured blocked literals + const blockedLiterals = commitConfig.diff.block.literals; + + // Configured blocked patterns + const blockedPatterns = commitConfig.diff.block.patterns; + + // Configured blocked providers + const blockedProviders = Object.values(commitConfig.diff.block.providers); + + // Find all instances of blocked literals in diff... + const positiveLiterals = blockedLiterals.map((literal) => + diff.toLowerCase().includes(literal.toLowerCase()), + ); + + // Find all instances of blocked patterns in diff... + const positivePatterns = blockedPatterns.map((pattern) => diff.match(new RegExp(pattern, 'gi'))); + + // Find all instances of blocked providers in diff... + const positiveProviders = blockedProviders.map((pattern) => + diff.match(new RegExp(pattern, 'gi')), + ); + + console.log({ positiveLiterals }); + console.log({ positivePatterns }); + console.log({ positiveProviders }); + + // Flatten any positive literal results into a 1D array... + const literalMatches = positiveLiterals.flat().filter((result) => !!result); + + // Flatten any positive pattern results into a 1D array... + const patternMatches = positivePatterns.flat().filter((result) => !!result); + + // Flatten any positive pattern results into a 1D array... + const providerMatches = + organization && privateOrganizations.includes(organization) // Return empty results for private organizations + ? [] + : positiveProviders.flat().filter((result) => !!result); + + console.log({ literalMatches }); + console.log({ patternMatches }); + console.log({ providerMatches }); + + // Diff matches configured block pattern(s) + if (literalMatches.length || patternMatches.length || providerMatches.length) { + console.log('Diff is blocked via configured literals/patterns/providers...'); + return false; + } + + return true; +}; + +const exec = async (req, action) => { + const step = new Step('scanDiff'); + + const { steps, commitFrom, commitTo } = action; + console.log(`Scanning diff: ${commitFrom}:${commitTo}`); + + const diff = steps.find((s) => s.stepName === 'diff')?.content; + + const legalDiff = isDiffLegal(diff, action.project); + + if (!legalDiff) { + console.log(`The following diff is illegal: ${commitFrom}:${commitTo}`); + + step.error = true; + step.log(`The following diff is illegal: ${commitFrom}:${commitTo}`); + step.setError( + '\n\n\n\nYour push has been blocked.\nPlease ensure your code does not contain sensitive information or URLs.\n\n\n', + ); + + action.addStep(step); + return action; + } + + action.addStep(step); + return action; +}; + +exec.displayName = 'scanDiff.exec'; +exports.exec = exec; diff --git a/src/proxy/processors/push-action/writePack.js b/src/proxy/processors/push-action/writePack.js index 8ee2bf5e..a2a35c4e 100644 --- a/src/proxy/processors/push-action/writePack.js +++ b/src/proxy/processors/push-action/writePack.js @@ -1,4 +1,4 @@ -const execSync = require('child_process').execSync; +const spawnSync = require('child_process').spawnSync; const Step = require('../../actions').Step; const exec = async (req, action) => { @@ -7,19 +7,21 @@ const exec = async (req, action) => { const cmd = `git receive-pack ${action.repoName}`; step.log(`executing ${cmd}`); - const content = execSync(`git receive-pack ${action.repoName}`, { + const content = spawnSync('git', ['receive-pack', action.repoName], { cwd: action.proxyGitPath, input: req.body, - }).toString('utf-8'); + encoding: 'utf-8', + }).stdout; + step.log(content); step.setContent(content); } catch (e) { step.setError(e.toString('utf-8')); throw e; } finally { action.addStep(step); - return action; } + return action; }; exec.displayName = 'writePack.exec'; diff --git a/src/proxy/routes/index.js b/src/proxy/routes/index.js index cc9bf46c..50a5d53c 100644 --- a/src/proxy/routes/index.js +++ b/src/proxy/routes/index.js @@ -1,9 +1,8 @@ -/* eslint-disable max-len */ const express = require('express'); const proxy = require('express-http-proxy'); -// eslint-disable-next-line new-cap -const router = express.Router(); +const router = new express.Router(); const chain = require('../chain'); +const config = require('../../config'); /** * For a given Git HTTP request destined for a GitHub repo, @@ -33,12 +32,7 @@ const stripGitHubFromGitPath = (url) => { */ const validGitRequest = (url, headers) => { const { 'user-agent': agent, accept } = headers; - if ( - [ - '/info/refs?service=git-upload-pack', - '/info/refs?service=git-receive-pack', - ].includes(url) - ) { + if (['/info/refs?service=git-upload-pack', '/info/refs?service=git-receive-pack'].includes(url)) { // https://www.git-scm.com/docs/http-protocol#_discovering_references // We can only filter based on User-Agent since the Accept header is not // sent in this request @@ -53,7 +47,8 @@ const validGitRequest = (url, headers) => { router.use( '/', - proxy('https://github.com', { + proxy(config.getProxyUrl(), { + preserveHostHdr: false, filter: async function (req, res) { try { console.log('request url: ', req.url); @@ -93,6 +88,8 @@ router.use( const packetMessage = handleMessage(message); + console.log(req.headers); + res.status(200).send(packetMessage); return false; @@ -104,20 +101,31 @@ router.use( return false; } }, - userResDecorator: function (proxyRes, proxyResData) { - // const data = proxyResData; - // const ts = Date.now(); - // fs.writeFileSync(`./.logs/responses/${ts}.${proxyRes.statusCode}.status`, proxyRes.statusCode); - // fs.writeFileSync(`./.logs/responses/${ts}.headers.json`, JSON.stringify(proxyRes.headers)); - // fs.writeFileSync(`./.logs/responses/${ts}.raw`, data); - // fs.writeFileSync(`./.logs/responses/${ts}.txt`, data.toString('utf-8')); - return proxyResData; + proxyReqPathResolver: (req) => { + const url = config.getProxyUrl() + req.originalUrl; + console.log('Sending request to ' + url); + return url; + }, + proxyReqOptDecorator: function (proxyReqOpts, srcReq) { + return proxyReqOpts; + }, + + proxyReqBodyDecorator: function (bodyContent, srcReq) { + if (srcReq.method === 'GET') { + return ''; + } + return bodyContent; + }, + + proxyErrorHandler: function (err, res, next) { + console.log(`ERROR=${err}`); + next(err); }, }), ); -const handleMessage = async (message) => { - const errorMessage = `ERR\t${message}`; +const handleMessage = (message) => { + const errorMessage = `\t${message}`; const len = 6 + new TextEncoder().encode(errorMessage).length; const prefix = len.toString(16); diff --git a/src/routes.js b/src/routes.jsx similarity index 68% rename from src/routes.js rename to src/routes.jsx index 07666468..526b452a 100644 --- a/src/routes.js +++ b/src/routes.jsx @@ -1,6 +1,3 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ - /* ! ========================================================= @@ -18,29 +15,32 @@ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ -import Dashboard from '@material-ui/icons/Dashboard'; + import Person from '@material-ui/icons/Person'; -import DashboardPage from './ui/views/Dashboard/Dashboard.jsx'; -import OpenPushRequests from './ui/views/OpenPushRequests/OpenPushRequests.jsx'; -import PushDetails from './ui/views/PushDetails/PushDetails.jsx'; -import User from './ui/views/User/User.jsx'; -import UserList from './ui/views/UserList/UserList.jsx'; -import RepoDetails from './ui/views/RepoDetails/RepoDetails.jsx'; -import RepoList from './ui/views/RepoList/RepoList.jsx'; +import OpenPushRequests from './ui/views/OpenPushRequests/OpenPushRequests'; +import PushDetails from './ui/views/PushDetails/PushDetails'; +import User from './ui/views/User/User'; +import UserList from './ui/views/UserList/UserList'; +import RepoDetails from './ui/views/RepoDetails/RepoDetails'; +import RepoList from './ui/views/RepoList/RepoList'; + +import { RepoIcon } from '@primer/octicons-react'; + +import { Group, AccountCircle, Dashboard } from '@material-ui/icons'; const dashboardRoutes = [ { - path: '/dashboard', - name: 'Dashboard', - icon: Dashboard, - component: DashboardPage, + path: '/repo', + name: 'Repositories', + icon: RepoIcon, + component: RepoList, layout: '/admin', visible: true, }, { path: '/push', - name: 'Open Push Requests', - icon: Person, + name: 'Dashboard', + icon: Dashboard, component: OpenPushRequests, layout: '/admin', visible: true, @@ -55,15 +55,15 @@ const dashboardRoutes = [ }, { path: '/profile', - name: 'My Profile', - icon: Person, + name: 'My Account', + icon: AccountCircle, component: User, layout: '/admin', visible: true, }, { path: '/user/:id', - name: 'User Details', + name: 'User', icon: Person, component: User, layout: '/admin', @@ -77,18 +77,10 @@ const dashboardRoutes = [ layout: '/admin', visible: false, }, - { - path: '/repo', - name: 'Repositories', - icon: Person, - component: RepoList, - layout: '/admin', - visible: true, - }, { path: '/user', - name: 'User List', - icon: Person, + name: 'Users', + icon: Group, component: UserList, layout: '/admin', visible: true, diff --git a/src/service/emailSender.js b/src/service/emailSender.js new file mode 100644 index 00000000..aa1ddeee --- /dev/null +++ b/src/service/emailSender.js @@ -0,0 +1,20 @@ +const nodemailer = require('nodemailer'); +const config = require('../config'); + +exports.sendEmail = async (from, to, subject, body) => { + const smtpHost = config.getSmtpHost(); + const smtpPort = config.getSmtpPort(); + const transporter = nodemailer.createTransport({ + host: smtpHost, + port: smtpPort, + }); + + const email = `${body}`; + const info = await transporter.sendMail({ + from, + to, + subject, + html: email, + }); + console.log('Message sent %s', info.messageId); +}; diff --git a/src/service/index.js b/src/service/index.js index aa9bd8b4..ea6ef802 100644 --- a/src/service/index.js +++ b/src/service/index.js @@ -3,6 +3,9 @@ const session = require('express-session'); const http = require('http'); const cors = require('cors'); const app = express(); +const path = require('path'); +const config = require('../config'); +const db = require('../db'); const rateLimit = require('express-rate-limit'); const lusca = require('lusca'); @@ -16,34 +19,57 @@ const { GIT_PROXY_UI_PORT: uiPort } = require('../config/env').Vars; const _httpServer = http.createServer(app); const corsOptions = { - origin: 'http://localhost:3000', - methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', - preflightContinue: false, credentials: true, + origin: true, }; const start = async () => { // configuration of passport is async - // Before we can bind the routes - we need the passport + // Before we can bind the routes - we need the passport strategy const passport = await require('./passport').configure(); const routes = require('./routes'); + const absBuildPath = path.join(__dirname, '../../build'); app.use(cors(corsOptions)); + app.set('trust proxy', 1); app.use(limiter); app.use( session({ - secret: 'keyboard cat', + store: config.getDatabase().type === 'mongo' ? db.getSessionStore(session) : null, + secret: config.getCookieSecret(), resave: false, saveUninitialized: false, + cookie: { + secure: 'auto', + httpOnly: true, + maxAge: config.getSessionMaxAgeHours() * 60 * 60 * 1000, + }, }), ); + if (config.getCSRFProtection() && process.env.NODE_ENV !== 'test') { + app.use( + lusca({ + csrf: { + cookie: { name: 'csrf' }, + }, + hsts: { maxAge: 31536000, includeSubDomains: true, preload: true }, + nosniff: true, + referrerPolicy: 'same-origin', + xframe: 'SAMEORIGIN', + xssProtection: true, + }), + ); + } app.use(passport.initialize()); app.use(passport.session()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use('/', routes); - app.use(lusca.csrf()); + app.use('/', express.static(absBuildPath)); + app.get('/*', (req, res) => { + res.sendFile(path.join(`${absBuildPath}/index.html`)); + }); - await _httpServer.listen(uiPort); + _httpServer.listen(uiPort); console.log(`Service Listening on ${uiPort}`); app.emit('ready'); diff --git a/src/service/passport/activeDirectory.js b/src/service/passport/activeDirectory.js index c8b98c7d..466f57b1 100644 --- a/src/service/passport/activeDirectory.js +++ b/src/service/passport/activeDirectory.js @@ -1,30 +1,59 @@ -/* eslint-disable max-len */ const configure = () => { const passport = require('passport'); const ActiveDirectoryStrategy = require('passport-activedirectory'); + const config = require('../../config').getAuthentication(); + const adConfig = config.adConfig; + const db = require('../../db'); + const userGroup = config.userGroup; + const adminGroup = config.adminGroup; + const domain = config.domain; + console.log(`AD User Group: ${userGroup}, AD Admin Group: ${adminGroup}`); + + const ldaphelper = require('./ldaphelper'); passport.use( new ActiveDirectoryStrategy( { + passReqToCallback: true, integrated: false, - ldap: { - url: 'ldap://20.39.221.61', - baseDN: 'DC=rebeladmin,DC=com', - username: 'ta1234@rebeladmin.com', - password: 'London1234', - }, + ldap: adConfig, }, - function (profile, ad, done) { - ad.isUserMemberOf(profile._json.dn, 'proxy_users', (err, isMember) => { - if (isMember) { - profile.id = profile._json.userPrincipalName; - } else { - } - if (err) { - return done(err); - } - return done(null, profile); - }); + async function (req, profile, ad, done) { + profile.username = profile._json.sAMAccountName.toLowerCase(); + profile.email = profile._json.mail; + profile.id = profile.username; + req.user = profile; + + console.log( + `passport.activeDirectory: resolved login ${ + profile._json.userPrincipalName + }, profile=${JSON.stringify(profile)}`, + ); + // First check to see if the user is in the usergroups + const isUser = await ldaphelper.isUserInAdGroup(profile.username, domain, userGroup); + + if (!isUser) { + const message = `User it not a member of ${userGroup}`; + return done(message, null); + } + + // Now check if the user is an admin + const isAdmin = await ldaphelper.isUserInAdGroup(profile.username, domain, adminGroup); + + profile.admin = isAdmin; + console.log(`passport.activeDirectory: ${profile.username} admin=${isAdmin}`); + + const user = { + username: profile.username, + admin: isAdmin, + email: profile._json.mail, + displayName: profile.displayName, + title: profile._json.title, + }; + + await db.updateUser(user); + + return done(null, user); }, ), ); @@ -37,7 +66,6 @@ const configure = () => { done(null, user); }); - passport.type = 'ActiveDirectory'; return passport; }; diff --git a/src/service/passport/index.js b/src/service/passport/index.js index 536ee3cf..92a1c0bd 100644 --- a/src/service/passport/index.js +++ b/src/service/passport/index.js @@ -17,6 +17,7 @@ const configure = async () => { default: throw Error(`uknown authentication type ${type}`); } + _passport.type = authenticationConfig.type; return _passport; }; diff --git a/src/service/passport/ldaphelper.js b/src/service/passport/ldaphelper.js new file mode 100644 index 00000000..886b2c4a --- /dev/null +++ b/src/service/passport/ldaphelper.js @@ -0,0 +1,27 @@ +const axios = require('axios'); +const thirdpartyApiConfig = require('../../config').getAPIs(); +const client = axios.create({ + responseType: 'json', + headers: { + 'content-type': 'application/json', + }, +}); + +const isUserInAdGroup = (id, domain, name) => { + const url = String(thirdpartyApiConfig.ls.userInADGroup) + .replace('', domain) + .replace('', name) + .replace('', id); + + console.log(`checking if user is in group ${url}`); + return client + .get(url) + .then((res) => res.data) + .catch(() => { + return false; + }); +}; + +module.exports = { + isUserInAdGroup, +}; diff --git a/src/service/passport/local.js b/src/service/passport/local.js index c1c28d72..9d97aa82 100644 --- a/src/service/passport/local.js +++ b/src/service/passport/local.js @@ -1,4 +1,4 @@ -const passwordHash = require('password-hash'); +const bcrypt = require('bcrypt'); /* eslint-disable max-len */ const configure = async () => { const passport = require('passport'); @@ -8,12 +8,12 @@ const configure = async () => { passport.use( new Strategy((username, password, cb) => { db.findUser(username) - .then((user) => { + .then(async (user) => { if (!user) { return cb(null, false); } - const passwordCorrect = passwordHash.verify(password, user.password); + const passwordCorrect = await bcrypt.compare(password, user.password); if (!passwordCorrect) { return cb(null, false); @@ -43,16 +43,7 @@ const configure = async () => { const admin = await db.findUser('admin'); if (!admin) { - await db.createUser( - 'admin', - 'admin', - 'admin@place.com', - 'none', - true, - true, - true, - true, - ); + await db.createUser('admin', 'admin', 'admin@place.com', 'none', true, true, true, true); } passport.type = 'local'; diff --git a/src/service/routes/auth.js b/src/service/routes/auth.js index b0347ec7..ce6a4243 100644 --- a/src/service/routes/auth.js +++ b/src/service/routes/auth.js @@ -3,34 +3,45 @@ const router = new express.Router(); const passport = require('../passport').getPassport(); const db = require('../../db'); const passportType = passport.type; -const generator = require('generate-password'); -const passwordHash = require('password-hash'); - -router.post('/login', passport.authenticate(passportType), (req, res) => { - res.send({ - message: 'success', - }); -}); router.get('/', (req, res) => { res.status(200).json({ login: { action: 'post', - uri: 'auth/login', + uri: '/api/auth/login', }, profile: { action: 'get', - uri: 'auth/profile', + uri: '/api/auth/profile', }, logout: { action: 'post', - uri: 'auth/logout', + uri: '/api/auth/logout', }, }); }); +router.post('/login', passport.authenticate(passportType), async (req, res) => { + try { + console.log( + `serivce.routes.auth.login: user logged in, username=${ + req.user.username + } profile=${JSON.stringify(req.user)}`, + ); + } catch (e) { + console.log(`service.routes.auth.login: Error logging user in ${JSON.stringify(e)}`); + res.status(500).send('Failed to login').end(); + return; + } + res.send({ + message: 'success', + user: req.user, + }); +}); + // when login is successful, retrieve user info router.get('/success', (req, res) => { + console.log('authenticated' + JSON.stringify(req.user)); if (req.user) { res.json({ success: true, @@ -52,49 +63,44 @@ router.get('failed', (req, res) => { }); router.post('/logout', (req, res, next) => { - req.logout((err) => { - if (err) { - return next(err); - } - res.redirect('/'); + req.logout(req.user, (err) => { + if (err) return next(err); }); + res.clearCookie('connect.sid'); + res.send({ isAuth: req.isAuthenticated(), user: req.user }); }); -router.get('/profile', (req, res) => { +router.get('/profile', async (req, res) => { if (req.user) { - const user = JSON.parse(JSON.stringify(req.user)); - delete user.password; - res.send(user); + const userVal = await db.findUser(req.user.username); + delete userVal.password; + res.send(userVal); } else { res.status(401).end(); } }); -router.post('/profile', async (req, res) => { +router.post('/gitAccount', async (req, res) => { if (req.user) { try { - const password = generator.generate({ - length: 10, - numbers: true, - }); + let login = + req.body.username == null || req.body.username == 'undefined' + ? req.body.id + : req.body.username; - console.log(JSON.stringify(req.body)); + login = login.split('@')[0]; - const newUser = await db.createUser( - req.body.username, - password, - req.body.email, - req.body.gitAccount, - req.body.admin, - ); + const user = await db.findUser(login); - res.send(newUser); + console.log('Adding gitAccount' + req.body.gitAccount); + user.gitAccount = req.body.gitAccount; + db.updateUser(user); + res.status(200).end(); } catch (e) { - console.log(e); res .status(500) .send({ - message: e.message, + message: 'An error occurred', }) .end(); } @@ -103,30 +109,15 @@ router.post('/profile', async (req, res) => { } }); -router.post('/password', async (req, res) => { +router.get('/userLoggedIn', async (req, res) => { if (req.user) { - try { - const user = await db.findUser(req.user.username); - - if (passwordHash.verify(req.body.oldPassword, user.password)) { - user.password = passwordHash.generate(req.body.newPassword); - user.changePassword = false; - db.updateUser(user); - res.status(200).end(); - } else { - throw new Error('current password did not match the given'); - } - } catch (e) { - res - .status(500) - .send({ - message: 'An error occurred', - }) - .end(); - } + const user = JSON.parse(JSON.stringify(req.user)); + delete user.password; + const login = user.username; + const userVal = await db.findUser(login); + res.send(userVal); } else { res.status(401).end(); } }); - module.exports = router; diff --git a/src/service/routes/config.js b/src/service/routes/config.js new file mode 100644 index 00000000..82712ca4 --- /dev/null +++ b/src/service/routes/config.js @@ -0,0 +1,18 @@ +const express = require('express'); +const router = new express.Router(); + +const config = require('../../config'); + +router.get('/attestation', function ({ res }) { + res.send(config.getAttestationConfig()); +}); + +router.get('/urlShortener', function ({ res }) { + res.send(config.getURLShortener()); +}); + +router.get('/contactEmail', function ({ res }) { + res.send(config.getContactEmail()); +}); + +module.exports = router; diff --git a/src/service/routes/home.js b/src/service/routes/home.js index c5ba6c19..ce11503f 100644 --- a/src/service/routes/home.js +++ b/src/service/routes/home.js @@ -4,7 +4,7 @@ const router = new express.Router(); const resource = { healthcheck: '/api/v1/healthcheck', push: '/api/v1/push', - auth: '/api/v1/auth', + auth: '/api/auth', }; router.get('/', function (req, res) { diff --git a/src/service/routes/index.js b/src/service/routes/index.js index 750aecb6..a7529a4a 100644 --- a/src/service/routes/index.js +++ b/src/service/routes/index.js @@ -5,13 +5,15 @@ const home = require('./home'); const repo = require('./repo'); const users = require('./users'); const healthcheck = require('./healthcheck'); +const config = require('./config'); const router = new express.Router(); -router.use('/', home); -router.use('/auth', auth); +router.use('/api', home); +router.use('/api/auth', auth); router.use('/api/v1/healthcheck', healthcheck); router.use('/api/v1/push', push); router.use('/api/v1/repo', repo); router.use('/api/v1/user', users); +router.use('/api/v1/config', config); module.exports = router; diff --git a/src/service/routes/push.js b/src/service/routes/push.js index 8898ba42..9750375c 100644 --- a/src/service/routes/push.js +++ b/src/service/routes/push.js @@ -3,44 +3,32 @@ const router = new express.Router(); const db = require('../../db'); router.get('/', async (req, res) => { - if (req.user) { - const query = { - type: 'push', - }; - - for (const k in req.query) { - if (!k) continue; - - if (k === 'limit') continue; - if (k === 'skip') continue; - let v = req.query[k]; - if (v === 'false') v = false; - if (v === 'true') v = true; - query[k] = v; - } + const query = { + type: 'push', + }; - res.send(await db.getPushes(query)); - } else { - res.status(401).send({ - message: 'not logged in', - }); + for (const k in req.query) { + if (!k) continue; + + if (k === 'limit') continue; + if (k === 'skip') continue; + let v = req.query[k]; + if (v === 'false') v = false; + if (v === 'true') v = true; + query[k] = v; } + + res.send(await db.getPushes(query)); }); router.get('/:id', async (req, res) => { - if (req.user) { - const id = req.params.id; - push = await db.getPush(id); - if (push) { - res.send(push); - } else { - res.status(404).send({ - message: 'not found', - }); - } + const id = req.params.id; + const push = await db.getPush(id); + if (push) { + res.send(push); } else { - res.status(401).send({ - message: 'not logged in', + res.status(404).send({ + message: 'not found', }); } }); @@ -48,8 +36,43 @@ router.get('/:id', async (req, res) => { router.post('/:id/reject', async (req, res) => { if (req.user) { const id = req.params.id; - const result = await db.reject(id); - res.send(result); + console.log({ id }); + + // Get the push request + const push = await db.getPush(id); + console.log({ push }); + + // Get the Internal Author of the push via their Git Account name + const gitAccountauthor = push.user; + const list = await db.getUsers({ gitAccount: gitAccountauthor }); + console.log({ list }); + + if (list.length === 0) { + res.status(401).send({ + message: `The git account ${gitAccountauthor} could not be found`, + }); + return; + } + + if (list[0].username.toLowerCase() === req.user.username.toLowerCase() && !list[0].admin) { + res.status(401).send({ + message: `Cannot reject your own changes`, + }); + return; + } + + const isAllowed = await db.canUserApproveRejectPush(id, req.user.username); + console.log({ isAllowed }); + + if (isAllowed) { + const result = await db.reject(id); + console.log(`user ${req.user.username} rejected push request for ${id}`); + res.send(result); + } else { + res.status(401).send({ + message: 'User is not authorised to reject changes', + }); + } } else { res.status(401).send({ message: 'not logged in', @@ -58,13 +81,78 @@ router.post('/:id/reject', async (req, res) => { }); router.post('/:id/authorise', async (req, res) => { - if (req.user) { + console.log({ req }); + + const questions = req.body.params?.attestation; + console.log({ questions }); + + const attestationComplete = questions?.every((question) => !!question.checked); + console.log({ attestationComplete }); + + if (req.user && attestationComplete) { const id = req.params.id; - const result = await db.authorise(id); - res.send(result); + console.log({ id }); + + // Get the push request + const push = await db.getPush(id); + console.log({ push }); + + // Get the Internal Author of the push via their Git Account name + const gitAccountauthor = push.user; + const list = await db.getUsers({ gitAccount: gitAccountauthor }); + console.log({ list }); + + if (list.length === 0) { + res.status(401).send({ + message: `The git account ${gitAccountauthor} could not be found`, + }); + return; + } + + if (list[0].username.toLowerCase() === req.user.username.toLowerCase() && !list[0].admin) { + res.status(401).send({ + message: `Cannot approve your own changes`, + }); + return; + } + + // If we are not the author, now check that we are allowed to authorise on this + // repo + const isAllowed = await db.canUserApproveRejectPush(id, req.user.username); + if (isAllowed) { + console.log(`user ${req.user.username} approved push request for ${id}`); + + const reviewerList = await db.getUsers({ username: req.user.username }); + console.log({ reviewerList }); + + const reviewerGitAccount = reviewerList[0].gitAccount; + console.log({ reviewerGitAccount }); + + if (!reviewerGitAccount) { + res.status(401).send({ + message: 'You must associate a GitHub account with your user before approving...', + }); + return; + } + + const attestation = { + questions, + timestamp: new Date(), + reviewer: { + username: req.user.username, + gitAccount: reviewerGitAccount, + }, + }; + const result = await db.authorise(id, attestation); + res.send(result); + } else { + res.status(401).send({ + message: `user ${req.user.username} not authorised to approve push's on this project`, + }); + } } else { res.status(401).send({ - message: 'not logged in', + message: 'You are unauthorized to perform this action...', }); } }); @@ -72,8 +160,20 @@ router.post('/:id/authorise', async (req, res) => { router.post('/:id/cancel', async (req, res) => { if (req.user) { const id = req.params.id; - const result = await db.cancel(id); - res.send(result); + + const isAllowed = await db.canUserCancelPush(id, req.user.username); + + if (isAllowed) { + const result = await db.cancel(id); + console.log(`user ${req.user.username} canceled push request for ${id}`); + res.send(result); + } else { + console.log(`user ${req.user.username} not authorised to cancel push request for ${id}`); + res.status(401).send({ + message: + 'User ${req.user.username)} not authorised to cancel push requests on this project.', + }); + } } else { res.status(401).send({ message: 'not logged in', diff --git a/src/service/routes/repo.js b/src/service/routes/repo.js index 2c38fb6e..a50c21f2 100644 --- a/src/service/routes/repo.js +++ b/src/service/routes/repo.js @@ -3,60 +3,37 @@ const router = new express.Router(); const db = require('../../db'); router.get('/', async (req, res) => { - if (req.user) { - const query = { - type: 'push', - }; - - for (const k in req.query) { - if (!k) continue; - - if (k === 'limit') continue; - if (k === 'skip') continue; - let v = req.query[k]; - if (v === 'false') v = false; - if (v === 'true') v = true; - query[k] = v; - } - - res.send(await db.getRepos(query)); - } else { - res.status(401).send({ - message: 'not logged in', - }); + const query = { + type: 'push', + }; + + for (const k in req.query) { + if (!k) continue; + + if (k === 'limit') continue; + if (k === 'skip') continue; + let v = req.query[k]; + if (v === 'false') v = false; + if (v === 'true') v = true; + query[k] = v; } -}); -router.get('/:name', async (req, res) => { - if (req.user) { - const name = req.params.name; - res.send(await db.getRepo(name)); - } else { - res.status(401).send({ - message: 'not logged in', - }); - } + res.send(await db.getRepos(query)); }); -router.post('/', async (req, res) => { - if (req.user) { - await db.createRepo(req.body); - res.send({ message: 'created' }); - } else { - res.status(401).send({ - message: 'not logged in', - }); - } +router.get('/:name', async (req, res) => { + const name = req.params.name; + res.send(await db.getRepo(name)); }); router.patch('/:name/user/push', async (req, res) => { - if (req.user) { + if (req.user && req.user.admin) { const repoName = req.params.name; - const username = req.body.username; + const username = req.body.username.toLowerCase(); const user = await db.findUser(username); if (!user) { - res.status(400).send({ error: `user ${username} does not exist` }); + res.status(400).send({ error: 'User does not exist' }); return; } @@ -64,19 +41,19 @@ router.patch('/:name/user/push', async (req, res) => { res.send({ message: 'created' }); } else { res.status(401).send({ - message: 'not logged in', + message: 'You are not authorised to perform this action...', }); } }); router.patch('/:name/user/authorise', async (req, res) => { - if (req.user) { + if (req.user && req.user.admin) { const repoName = req.params.name; const username = req.body.username; const user = await db.findUser(username); if (!user) { - res.status(400).send({ error: `user ${username} does not exist` }); + res.status(400).send({ error: 'User does not exist' }); return; } @@ -84,19 +61,19 @@ router.patch('/:name/user/authorise', async (req, res) => { res.send({ message: 'created' }); } else { res.status(401).send({ - message: 'not logged in', + message: 'You are not authorised to perform this action...', }); } }); router.delete('/:name/user/authorise/:username', async (req, res) => { - if (req.user) { + if (req.user && req.user.admin) { const repoName = req.params.name; const username = req.params.username; const user = await db.findUser(username); if (!user) { - res.status(400).send({ error: `user ${username} does not exist` }); + res.status(400).send({ error: 'User does not exist' }); return; } @@ -104,19 +81,19 @@ router.delete('/:name/user/authorise/:username', async (req, res) => { res.send({ message: 'created' }); } else { res.status(401).send({ - message: 'not logged in', + message: 'You are not authorised to perform this action...', }); } }); router.delete('/:name/user/push/:username', async (req, res) => { - if (req.user) { + if (req.user && req.user.admin) { const repoName = req.params.name; const username = req.params.username; const user = await db.findUser(username); if (!user) { - res.status(400).send({ error: `user ${username} does not exist` }); + res.status(400).send({ error: 'User does not exist' }); return; } @@ -124,7 +101,42 @@ router.delete('/:name/user/push/:username', async (req, res) => { res.send({ message: 'created' }); } else { res.status(401).send({ - message: 'not logged in', + message: 'You are not authorised to perform this action...', + }); + } +}); + +router.delete('/:name/delete', async (req, res) => { + if (req.user.admin) { + const repoName = req.params.name; + + await db.deleteRepo(repoName); + res.send({ message: 'deleted' }); + } else { + res.status(401).send({ + message: 'You are not authorised to perform this action...', + }); + } +}); + +router.post('/', async (req, res) => { + if (req.user && req.user.admin) { + const repo = await db.getRepo(req.body.name); + if (repo) { + res.status(409).send({ + message: 'Repository already exists!', + }); + } else { + try { + await db.createRepo(req.body); + res.send({ message: 'created' }); + } catch { + res.send('Failed to create repository'); + } + } + } else { + res.status(401).send({ + message: 'You are not authorised to perform this action...', }); } }); diff --git a/src/service/routes/users.js b/src/service/routes/users.js index 24125ec9..d25bd84d 100644 --- a/src/service/routes/users.js +++ b/src/service/routes/users.js @@ -3,45 +3,26 @@ const router = new express.Router(); const db = require('../../db'); router.get('/', async (req, res) => { - if (req.user) { - const query = { - type: 'push', - }; + const query = {}; - for (const k in req.query) { - if (!k) continue; + console.log(`fetching users = query path =${JSON.stringify(req.query)}`); + for (const k in req.query) { + if (!k) continue; - if (k === 'limit') continue; - if (k === 'skip') continue; - let v = req.query[k]; - if (v === 'false') v = false; - if (v === 'true') v = true; - query[k] = v; - } - - res.send(await db.getUsers(query)); - } else { - res.status(401).send({ - message: 'not logged in', - }); + if (k === 'limit') continue; + if (k === 'skip') continue; + let v = req.query[k]; + if (v === 'false') v = false; + if (v === 'true') v = true; + query[k] = v; } + + res.send(await db.getUsers(query)); }); router.get('/:id', async (req, res) => { - const username = req.params.id; + const username = req.params.id.toLowerCase(); console.log(`Retrieving details for user: ${username}`); - if (!req.user) { - res.status(401).send({ - message: 'not logged in', - }); - return; - } - if (!req.user.admin) { - console.error(`Retrieving details for user: ${username} - NOT AUTHORISED`); - // User is not an admin and forbidden form seeing other user profiles - res.status(403).end(); - return; - } const data = await db.findUser(username); const user = JSON.parse(JSON.stringify(data)); delete user.password; diff --git a/src/ui/assets/css/material-dashboard-react.css b/src/ui/assets/css/material-dashboard-react.css index 33ab6021..fb45e8a6 100644 --- a/src/ui/assets/css/material-dashboard-react.css +++ b/src/ui/assets/css/material-dashboard-react.css @@ -39,10 +39,6 @@ text-anchor: start; } -.ct-label { - color: rgba(255, 255, 255, 0.7); -} - .ct-chart-line .ct-label, .ct-chart-bar .ct-label { display: block; @@ -54,6 +50,7 @@ } .ct-label { + color: rgba(255, 255, 255, 0.7); fill: rgba(0, 0, 0, 0.4); line-height: 1; } @@ -62,7 +59,7 @@ html * { -moz-osx-font-smoothing: grayscale; } body { - background-color: #eeeeee; + background-color: #fff; color: #3c4858; margin: 0; font-family: Roboto, Helvetica, Arial, sans-serif; @@ -72,7 +69,7 @@ body { blockquote footer:before, blockquote small:before { - content: "\2014 \00A0"; + content: '\2014 \00A0'; } small { @@ -111,11 +108,6 @@ h6 { font-weight: 500; } -body { - background-color: #eeeeee; - color: #3c4858; -} - blockquote p { font-style: italic; } @@ -127,29 +119,28 @@ h3, h4, h5, h6 { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif; font-weight: 300; line-height: 1.5em; } a { - color: #9c27b0; + color: #0969da; text-decoration: none; + font-weight: 500; } a:hover, a:focus { - color: #89229b; - text-decoration: none; -} - -legend { - border-bottom: 0; + color: #0969da; + text-decoration: underline; + font-weight: 500; } * { -webkit-tap-highlight-color: rgba(255, 255, 255, 0); -webkit-tap-highlight-color: transparent; + letter-spacing: normal !important; } *:focus { @@ -162,17 +153,18 @@ button:active, button:focus, button:hover, button::-moz-focus-inner, -input[type="reset"]::-moz-focus-inner, -input[type="button"]::-moz-focus-inner, -input[type="submit"]::-moz-focus-inner, +input[type='reset']::-moz-focus-inner, +input[type='button']::-moz-focus-inner, +input[type='submit']::-moz-focus-inner, select::-moz-focus-inner, -input[type="file"] > input[type="button"]::-moz-focus-inner { +input[type='file'] > input[type='button']::-moz-focus-inner { outline: 0 !important; } legend { margin-bottom: 20px; font-size: 21px; + border-bottom: 0; } output { @@ -233,17 +225,16 @@ footer ul li a:hover { top: 0; left: auto; right: 260px; - content: ""; + content: ''; z-index: 9999; overflow-x: hidden; } } .fixed-plugin { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; + font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif; font-weight: 300; line-height: 1.5em; position: fixed; - top: 180px; right: 0; width: 64px; background: rgba(0, 0, 0, 0.3); @@ -278,7 +269,6 @@ footer ul li a:hover { right: 80px; left: auto; width: 290px; - border-radius: 0.1875rem; padding: 0 10px; position: absolute; color: rgba(0, 0, 0, 0.87); @@ -432,7 +422,6 @@ footer ul li a:hover { display: block; max-height: 100px; overflow: hidden; - padding: 0; } .fixed-plugin .dropdown-menu > li > a.img-holder img { @@ -443,7 +432,6 @@ footer ul li a:hover { border-color: rgba(0, 187, 255, 0.53); } -.fixed-plugin .dropdown-menu > .active > a.img-holder, .fixed-plugin .dropdown-menu > .active > a.img-holder { border-color: #00bbff; background-color: #ffffff; @@ -476,7 +464,7 @@ footer ul li a:hover { .fixed-plugin .dropdown .dropdown-menu:before, .fixed-plugin .dropdown .dropdown-menu:after { - content: ""; + content: ''; display: inline-block; position: absolute; top: 46px; @@ -506,6 +494,3 @@ footer ul li a:hover { right: auto; left: 80px; } -* { - letter-spacing: normal !important; -} diff --git a/src/ui/assets/github/angular.png b/src/ui/assets/github/angular.png deleted file mode 100644 index 71469a76..00000000 Binary files a/src/ui/assets/github/angular.png and /dev/null differ diff --git a/src/ui/assets/github/chrome.png b/src/ui/assets/github/chrome.png deleted file mode 100644 index 6ba3616f..00000000 Binary files a/src/ui/assets/github/chrome.png and /dev/null differ diff --git a/src/ui/assets/github/dashboard.jpg b/src/ui/assets/github/dashboard.jpg deleted file mode 100644 index bbd65dda..00000000 Binary files a/src/ui/assets/github/dashboard.jpg and /dev/null differ diff --git a/src/ui/assets/github/edge.png b/src/ui/assets/github/edge.png deleted file mode 100644 index 4a4eb901..00000000 Binary files a/src/ui/assets/github/edge.png and /dev/null differ diff --git a/src/ui/assets/github/firefox.png b/src/ui/assets/github/firefox.png deleted file mode 100644 index cad280e4..00000000 Binary files a/src/ui/assets/github/firefox.png and /dev/null differ diff --git a/src/ui/assets/github/html.png b/src/ui/assets/github/html.png deleted file mode 100644 index 2b3d9d61..00000000 Binary files a/src/ui/assets/github/html.png and /dev/null differ diff --git a/src/ui/assets/github/map.jpg b/src/ui/assets/github/map.jpg deleted file mode 100644 index b5e471b7..00000000 Binary files a/src/ui/assets/github/map.jpg and /dev/null differ diff --git a/src/ui/assets/github/md-react.gif b/src/ui/assets/github/md-react.gif deleted file mode 100644 index fbf2d840..00000000 Binary files a/src/ui/assets/github/md-react.gif and /dev/null differ diff --git a/src/ui/assets/github/notifications.jpg b/src/ui/assets/github/notifications.jpg deleted file mode 100644 index 44ed884e..00000000 Binary files a/src/ui/assets/github/notifications.jpg and /dev/null differ diff --git a/src/ui/assets/github/opera.png b/src/ui/assets/github/opera.png deleted file mode 100644 index cdf33924..00000000 Binary files a/src/ui/assets/github/opera.png and /dev/null differ diff --git a/src/ui/assets/github/opt_md_angular_thumbnail.jpg b/src/ui/assets/github/opt_md_angular_thumbnail.jpg deleted file mode 100644 index 30ade38a..00000000 Binary files a/src/ui/assets/github/opt_md_angular_thumbnail.jpg and /dev/null differ diff --git a/src/ui/assets/github/opt_md_thumbnail.jpg b/src/ui/assets/github/opt_md_thumbnail.jpg deleted file mode 100644 index 4b2c44a0..00000000 Binary files a/src/ui/assets/github/opt_md_thumbnail.jpg and /dev/null differ diff --git a/src/ui/assets/github/opt_md_vue_thumbnail.jpg b/src/ui/assets/github/opt_md_vue_thumbnail.jpg deleted file mode 100644 index 88b9c8c0..00000000 Binary files a/src/ui/assets/github/opt_md_vue_thumbnail.jpg and /dev/null differ diff --git a/src/ui/assets/github/opt_mdr_thumbnail.jpg b/src/ui/assets/github/opt_mdr_thumbnail.jpg deleted file mode 100644 index f46730f1..00000000 Binary files a/src/ui/assets/github/opt_mdr_thumbnail.jpg and /dev/null differ diff --git a/src/ui/assets/github/react.svg b/src/ui/assets/github/react.svg deleted file mode 100644 index ea77a618..00000000 --- a/src/ui/assets/github/react.svg +++ /dev/null @@ -1,9 +0,0 @@ - - React Logo - - - - - - - diff --git a/src/ui/assets/github/safari.png b/src/ui/assets/github/safari.png deleted file mode 100644 index 84dc844a..00000000 Binary files a/src/ui/assets/github/safari.png and /dev/null differ diff --git a/src/ui/assets/github/tables.jpg b/src/ui/assets/github/tables.jpg deleted file mode 100644 index ceeb8317..00000000 Binary files a/src/ui/assets/github/tables.jpg and /dev/null differ diff --git a/src/ui/assets/github/userprofile.jpg b/src/ui/assets/github/userprofile.jpg deleted file mode 100644 index 7181b2f0..00000000 Binary files a/src/ui/assets/github/userprofile.jpg and /dev/null differ diff --git a/src/ui/assets/github/vuejs.png b/src/ui/assets/github/vuejs.png deleted file mode 100644 index 60e17006..00000000 Binary files a/src/ui/assets/github/vuejs.png and /dev/null differ diff --git a/src/ui/assets/img/apple-icon.png b/src/ui/assets/img/apple-icon.png deleted file mode 100644 index 4dd354ce..00000000 Binary files a/src/ui/assets/img/apple-icon.png and /dev/null differ diff --git a/src/ui/assets/img/cover.jpeg b/src/ui/assets/img/cover.jpeg deleted file mode 100644 index c70fee79..00000000 Binary files a/src/ui/assets/img/cover.jpeg and /dev/null differ diff --git a/src/ui/assets/img/faces/marc.jpg b/src/ui/assets/img/faces/marc.jpg deleted file mode 100644 index b84f3f06..00000000 Binary files a/src/ui/assets/img/faces/marc.jpg and /dev/null differ diff --git a/src/ui/assets/img/favicon.png b/src/ui/assets/img/favicon.png deleted file mode 100644 index 4dd354ce..00000000 Binary files a/src/ui/assets/img/favicon.png and /dev/null differ diff --git a/src/ui/assets/img/git-proxy.png b/src/ui/assets/img/git-proxy.png new file mode 100644 index 00000000..d340cde5 Binary files /dev/null and b/src/ui/assets/img/git-proxy.png differ diff --git a/src/ui/assets/img/mask.png b/src/ui/assets/img/mask.png deleted file mode 100644 index 4dd354ce..00000000 Binary files a/src/ui/assets/img/mask.png and /dev/null differ diff --git a/src/ui/assets/img/new_logo.png b/src/ui/assets/img/new_logo.png deleted file mode 100644 index 4dd354ce..00000000 Binary files a/src/ui/assets/img/new_logo.png and /dev/null differ diff --git a/src/ui/assets/img/reactlogo.png b/src/ui/assets/img/reactlogo.png deleted file mode 100644 index 4dd354ce..00000000 Binary files a/src/ui/assets/img/reactlogo.png and /dev/null differ diff --git a/src/ui/assets/img/sidebar-1.jpg b/src/ui/assets/img/sidebar-1.jpg deleted file mode 100644 index d60429be..00000000 Binary files a/src/ui/assets/img/sidebar-1.jpg and /dev/null differ diff --git a/src/ui/assets/img/sidebar-2.jpg b/src/ui/assets/img/sidebar-2.jpg deleted file mode 100644 index 14e29f3a..00000000 Binary files a/src/ui/assets/img/sidebar-2.jpg and /dev/null differ diff --git a/src/ui/assets/img/sidebar-3.jpg b/src/ui/assets/img/sidebar-3.jpg deleted file mode 100644 index c9dfa637..00000000 Binary files a/src/ui/assets/img/sidebar-3.jpg and /dev/null differ diff --git a/src/ui/assets/img/sidebar-4.jpg b/src/ui/assets/img/sidebar-4.jpg deleted file mode 100644 index 06c46ed9..00000000 Binary files a/src/ui/assets/img/sidebar-4.jpg and /dev/null differ diff --git a/src/ui/assets/img/tim_80x80.png b/src/ui/assets/img/tim_80x80.png deleted file mode 100644 index 5f2bb549..00000000 Binary files a/src/ui/assets/img/tim_80x80.png and /dev/null differ diff --git a/src/ui/assets/jss/material-dashboard-react.js b/src/ui/assets/jss/material-dashboard-react.js index b709fa9e..1cc9c2cd 100644 --- a/src/ui/assets/jss/material-dashboard-react.js +++ b/src/ui/assets/jss/material-dashboard-react.js @@ -1,4 +1,3 @@ -/* eslint-disable max-len */ /* ! ========================================================= @@ -30,22 +29,16 @@ const hexToRgb = (input) => { throw new Error('input is not a valid hex color.'); } if (input.length === 3) { - const first = input[0]; - const second = input[1]; - const last = input[2]; - input = first + first + second + second + last + last; + const firstOnThree = input[0]; + const secondOnThree = input[1]; + const lastOnThree = input[2]; + input = firstOnThree + firstOnThree + secondOnThree + secondOnThree + lastOnThree + lastOnThree; } input = input.toUpperCase(); const first = input[0] + input[1]; const second = input[2] + input[3]; const last = input[4] + input[5]; - return ( - parseInt(first, 16) + - ', ' + - parseInt(second, 16) + - ', ' + - parseInt(last, 16) - ); + return parseInt(first, 16) + ', ' + parseInt(second, 16) + ', ' + parseInt(last, 16); }; // ############################## @@ -71,11 +64,11 @@ const defaultFont = { lineHeight: '1.5em', }; -const primaryColor = ['#9c27b0', '#ab47bc', '#8e24aa', '#af2cc5']; +const primaryColor = ['#1a1a1a', '#1a1a1a', '#1a1a1a', '#1a1a1a']; const warningColor = ['#ff9800', '#ffa726', '#fb8c00', '#ffa21a']; const dangerColor = ['#f44336', '#ef5350', '#e53935', '#f55a4e']; const successColor = ['#4caf50', '#66bb6a', '#43a047', '#5cb860']; -const infoColor = ['#00acc1', '#26c6da', '#00acc1', '#00d3ee']; +const infoColor = ['#1a1a1a', '#1a1a1a', '#1a1a1a', '#1a1a1a']; const roseColor = ['#e91e63', '#ec407a', '#d81b60', '#eb3573']; const grayColor = [ '#999', @@ -155,33 +148,27 @@ const roseBoxShadow = { }; const warningCardHeader = { - background: - 'linear-gradient(60deg, ' + warningColor[1] + ', ' + warningColor[2] + ')', + background: 'linear-gradient(60deg, ' + warningColor[1] + ', ' + warningColor[2] + ')', ...warningBoxShadow, }; const successCardHeader = { - background: - 'linear-gradient(60deg, ' + successColor[1] + ', ' + successColor[2] + ')', + background: 'linear-gradient(60deg, ' + successColor[1] + ', ' + successColor[2] + ')', ...successBoxShadow, }; const dangerCardHeader = { - background: - 'linear-gradient(60deg, ' + dangerColor[1] + ', ' + dangerColor[2] + ')', + background: 'linear-gradient(60deg, ' + dangerColor[1] + ', ' + dangerColor[2] + ')', ...dangerBoxShadow, }; const infoCardHeader = { - background: - 'linear-gradient(60deg, ' + infoColor[1] + ', ' + infoColor[2] + ')', + background: 'linear-gradient(60deg, ' + infoColor[1] + ', ' + infoColor[2] + ')', ...infoBoxShadow, }; const primaryCardHeader = { - background: - 'linear-gradient(60deg, ' + primaryColor[1] + ', ' + primaryColor[2] + ')', + background: 'linear-gradient(60deg, ' + primaryColor[1] + ', ' + primaryColor[2] + ')', ...primaryBoxShadow, }; const roseCardHeader = { - background: - 'linear-gradient(60deg, ' + roseColor[1] + ', ' + roseColor[2] + ')', + background: 'linear-gradient(60deg, ' + roseColor[1] + ', ' + roseColor[2] + ')', ...roseBoxShadow, }; diff --git a/src/ui/assets/jss/material-dashboard-react/checkboxAdnRadioStyle.js b/src/ui/assets/jss/material-dashboard-react/checkboxAdnRadioStyle.js index eeb275bf..e6071970 100644 --- a/src/ui/assets/jss/material-dashboard-react/checkboxAdnRadioStyle.js +++ b/src/ui/assets/jss/material-dashboard-react/checkboxAdnRadioStyle.js @@ -1,8 +1,4 @@ -import { - primaryColor, - blackColor, - hexToRgb, -} from '../material-dashboard-react.js'; +import { primaryColor, blackColor, hexToRgb } from '../material-dashboard-react.js'; const checkboxAdnRadioStyle = { root: { diff --git a/src/ui/assets/jss/material-dashboard-react/components/cardHeaderStyle.js b/src/ui/assets/jss/material-dashboard-react/components/cardHeaderStyle.js index 9f33f43d..01096b78 100644 --- a/src/ui/assets/jss/material-dashboard-react/components/cardHeaderStyle.js +++ b/src/ui/assets/jss/material-dashboard-react/components/cardHeaderStyle.js @@ -1,4 +1,3 @@ -/* eslint-disable max-len */ import { warningCardHeader, successCardHeader, diff --git a/src/ui/assets/jss/material-dashboard-react/components/cardIconStyle.js b/src/ui/assets/jss/material-dashboard-react/components/cardIconStyle.js index d53c4b40..363eae65 100644 --- a/src/ui/assets/jss/material-dashboard-react/components/cardIconStyle.js +++ b/src/ui/assets/jss/material-dashboard-react/components/cardIconStyle.js @@ -1,4 +1,3 @@ -/* eslint-disable max-len */ import { warningCardHeader, successCardHeader, diff --git a/src/ui/assets/jss/material-dashboard-react/components/cardStyle.js b/src/ui/assets/jss/material-dashboard-react/components/cardStyle.js index 122f5d8a..5c850da6 100644 --- a/src/ui/assets/jss/material-dashboard-react/components/cardStyle.js +++ b/src/ui/assets/jss/material-dashboard-react/components/cardStyle.js @@ -1,8 +1,4 @@ -import { - blackColor, - whiteColor, - hexToRgb, -} from '../../material-dashboard-react.js'; +import { blackColor, whiteColor, hexToRgb } from '../../material-dashboard-react.js'; const cardStyle = { card: { diff --git a/src/ui/assets/jss/material-dashboard-react/components/footerStyle.js b/src/ui/assets/jss/material-dashboard-react/components/footerStyle.js index 8a012bcf..46b4cfa0 100644 --- a/src/ui/assets/jss/material-dashboard-react/components/footerStyle.js +++ b/src/ui/assets/jss/material-dashboard-react/components/footerStyle.js @@ -1,9 +1,4 @@ -import { - defaultFont, - container, - primaryColor, - grayColor, -} from '../../material-dashboard-react.js'; +import { defaultFont, container, primaryColor, grayColor } from '../../material-dashboard-react.js'; const footerStyle = { block: { diff --git a/src/ui/assets/jss/material-dashboard-react/components/headerLinksStyle.js b/src/ui/assets/jss/material-dashboard-react/components/headerLinksStyle.js index 19776c37..bdeb9dc4 100644 --- a/src/ui/assets/jss/material-dashboard-react/components/headerLinksStyle.js +++ b/src/ui/assets/jss/material-dashboard-react/components/headerLinksStyle.js @@ -1,8 +1,4 @@ -import { - defaultFont, - dangerColor, - whiteColor, -} from '../../material-dashboard-react.js'; +import { defaultFont, dangerColor, whiteColor } from '../../material-dashboard-react.js'; // eslint-disable-next-line max-len import dropdownStyle from '../dropdownStyle.js'; diff --git a/src/ui/assets/jss/material-dashboard-react/components/headerStyle.js b/src/ui/assets/jss/material-dashboard-react/components/headerStyle.js index ec8cda74..48439db2 100644 --- a/src/ui/assets/jss/material-dashboard-react/components/headerStyle.js +++ b/src/ui/assets/jss/material-dashboard-react/components/headerStyle.js @@ -8,7 +8,6 @@ import { warningColor, dangerColor, whiteColor, - grayColor, } from '../../material-dashboard-react.js'; const headerStyle = () => ({ @@ -19,9 +18,9 @@ const headerStyle = () => ({ marginBottom: '0', position: 'absolute', width: '100%', - paddingTop: '10px', + paddingTop: '27px', zIndex: '1029', - color: grayColor[7], + color: 'black', border: '0', borderRadius: '3px', padding: '10px 0', diff --git a/src/ui/assets/jss/material-dashboard-react/components/rtlHeaderLinksStyle.js b/src/ui/assets/jss/material-dashboard-react/components/rtlHeaderLinksStyle.js index 47f23b9e..8301e5c5 100644 --- a/src/ui/assets/jss/material-dashboard-react/components/rtlHeaderLinksStyle.js +++ b/src/ui/assets/jss/material-dashboard-react/components/rtlHeaderLinksStyle.js @@ -1,9 +1,4 @@ -/* eslint-disable max-len */ -import { - defaultFont, - dangerColor, - whiteColor, -} from '../../material-dashboard-react.js'; +import { defaultFont, dangerColor, whiteColor } from '../../material-dashboard-react.js'; import dropdownStyle from '../dropdownStyle.js'; diff --git a/src/ui/assets/jss/material-dashboard-react/components/sidebarStyle.js b/src/ui/assets/jss/material-dashboard-react/components/sidebarStyle.js index d702a7a5..fddf94b4 100644 --- a/src/ui/assets/jss/material-dashboard-react/components/sidebarStyle.js +++ b/src/ui/assets/jss/material-dashboard-react/components/sidebarStyle.js @@ -62,7 +62,7 @@ const sidebarStyle = (theme) => ({ }, logo: { position: 'relative', - padding: '15px 15px', + padding: '20px 20px', zIndex: '4', '&:after': { content: '""', diff --git a/src/ui/assets/jss/material-dashboard-react/components/tasksStyle.js b/src/ui/assets/jss/material-dashboard-react/components/tasksStyle.js index e40f06b9..2efdea8c 100644 --- a/src/ui/assets/jss/material-dashboard-react/components/tasksStyle.js +++ b/src/ui/assets/jss/material-dashboard-react/components/tasksStyle.js @@ -1,4 +1,3 @@ -/* eslint-disable max-len */ import { defaultFont, primaryColor, diff --git a/src/ui/assets/jss/material-dashboard-react/layouts/adminStyle.js b/src/ui/assets/jss/material-dashboard-react/layouts/adminStyle.js index a9631653..075819c1 100644 --- a/src/ui/assets/jss/material-dashboard-react/layouts/adminStyle.js +++ b/src/ui/assets/jss/material-dashboard-react/layouts/adminStyle.js @@ -1,8 +1,4 @@ -import { - drawerWidth, - transition, - container, -} from '../../material-dashboard-react.js'; +import { drawerWidth, transition, container } from '../../material-dashboard-react.js'; const appStyle = (theme) => ({ wrapper: { diff --git a/src/ui/assets/jss/material-dashboard-react/layouts/rtlStyle.js b/src/ui/assets/jss/material-dashboard-react/layouts/rtlStyle.js index e806d257..fcd43bbf 100644 --- a/src/ui/assets/jss/material-dashboard-react/layouts/rtlStyle.js +++ b/src/ui/assets/jss/material-dashboard-react/layouts/rtlStyle.js @@ -1,8 +1,4 @@ -import { - drawerWidth, - transition, - container, -} from '../../jss/material-dashboard-react.js'; +import { drawerWidth, transition, container } from '../../jss/material-dashboard-react.js'; const appStyle = (theme) => ({ wrapper: { diff --git a/src/ui/assets/jss/material-dashboard-react/views/dashboardStyle.js b/src/ui/assets/jss/material-dashboard-react/views/dashboardStyle.js index 3e9d478b..e8af4a7e 100644 --- a/src/ui/assets/jss/material-dashboard-react/views/dashboardStyle.js +++ b/src/ui/assets/jss/material-dashboard-react/views/dashboardStyle.js @@ -1,10 +1,4 @@ -/* eslint-disable quotes */ -import { - successColor, - whiteColor, - grayColor, - hexToRgb, -} from '../../material-dashboard-react.js'; +import { successColor, whiteColor, grayColor, hexToRgb } from '../../material-dashboard-react.js'; const dashboardStyle = { successText: { diff --git a/src/ui/assets/jss/material-dashboard-react/views/iconsStyle.js b/src/ui/assets/jss/material-dashboard-react/views/iconsStyle.js index ecfd4116..88de4458 100644 --- a/src/ui/assets/jss/material-dashboard-react/views/iconsStyle.js +++ b/src/ui/assets/jss/material-dashboard-react/views/iconsStyle.js @@ -1,9 +1,4 @@ -import { - boxShadow, - whiteColor, - grayColor, - hexToRgb, -} from '../../jss/material-dashboard-react.js'; +import { boxShadow, whiteColor, grayColor, hexToRgb } from '../../jss/material-dashboard-react.js'; const iconsStyle = { iframe: { @@ -32,7 +27,6 @@ const iconsStyle = { marginTop: '0px', minHeight: 'auto', fontWeight: '300', - // eslint-disable-next-line quotes fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", marginBottom: '3px', textDecoration: 'none', diff --git a/src/ui/assets/jss/material-dashboard-react/views/rtlStyle.js b/src/ui/assets/jss/material-dashboard-react/views/rtlStyle.js index dc786530..21527e71 100644 --- a/src/ui/assets/jss/material-dashboard-react/views/rtlStyle.js +++ b/src/ui/assets/jss/material-dashboard-react/views/rtlStyle.js @@ -1,10 +1,4 @@ -/* eslint-disable quotes */ -import { - successColor, - whiteColor, - grayColor, - hexToRgb, -} from '../../material-dashboard-react.js'; +import { successColor, whiteColor, grayColor, hexToRgb } from '../../material-dashboard-react.js'; const rtlStyle = { successText: { diff --git a/src/ui/components/Card/Card.jsx b/src/ui/components/Card/Card.jsx index 79c3191b..c3bb4013 100644 --- a/src/ui/components/Card/Card.jsx +++ b/src/ui/components/Card/Card.jsx @@ -1,10 +1,8 @@ -/* eslint-disable require-jsdoc */ -/* eslint-disable max-len */ import React from 'react'; import classNames from 'classnames'; import PropTypes from 'prop-types'; import { makeStyles } from '@material-ui/core/styles'; -import styles from '../../assets/jss/material-dashboard-react/components/cardStyle.js'; +import styles from '../../assets/jss/material-dashboard-react/components/cardStyle'; const useStyles = makeStyles(styles); diff --git a/src/ui/components/Card/CardAvatar.jsx b/src/ui/components/Card/CardAvatar.jsx index d68ce3c0..8814717e 100644 --- a/src/ui/components/Card/CardAvatar.jsx +++ b/src/ui/components/Card/CardAvatar.jsx @@ -1,10 +1,8 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import classNames from 'classnames'; import PropTypes from 'prop-types'; import { makeStyles } from '@material-ui/core/styles'; -import styles from '../../assets/jss/material-dashboard-react/components/cardAvatarStyle.js'; +import styles from '../../assets/jss/material-dashboard-react/components/cardAvatarStyle'; const useStyles = makeStyles(styles); diff --git a/src/ui/components/Card/CardBody.jsx b/src/ui/components/Card/CardBody.jsx index 5847fa0d..fa988f6c 100644 --- a/src/ui/components/Card/CardBody.jsx +++ b/src/ui/components/Card/CardBody.jsx @@ -1,10 +1,8 @@ -/* eslint-disable require-jsdoc */ -/* eslint-disable max-len */ import React from 'react'; import classNames from 'classnames'; import PropTypes from 'prop-types'; import { makeStyles } from '@material-ui/core/styles'; -import styles from '../../assets/jss/material-dashboard-react/components/cardBodyStyle.js'; +import styles from '../../assets/jss/material-dashboard-react/components/cardBodyStyle'; const useStyles = makeStyles(styles); diff --git a/src/ui/components/Card/CardFooter.jsx b/src/ui/components/Card/CardFooter.jsx index 10646499..4b6c02d4 100644 --- a/src/ui/components/Card/CardFooter.jsx +++ b/src/ui/components/Card/CardFooter.jsx @@ -1,10 +1,8 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import classNames from 'classnames'; import PropTypes from 'prop-types'; import { makeStyles } from '@material-ui/core/styles'; -import styles from '../../assets/jss/material-dashboard-react/components/cardFooterStyle.js'; +import styles from '../../assets/jss/material-dashboard-react/components/cardFooterStyle'; const useStyles = makeStyles(styles); diff --git a/src/ui/components/Card/CardHeader.jsx b/src/ui/components/Card/CardHeader.jsx index f680eab4..80227a10 100644 --- a/src/ui/components/Card/CardHeader.jsx +++ b/src/ui/components/Card/CardHeader.jsx @@ -1,10 +1,8 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import classNames from 'classnames'; import PropTypes from 'prop-types'; import { makeStyles } from '@material-ui/core/styles'; -import styles from '../../assets/jss/material-dashboard-react/components/cardHeaderStyle.js'; +import styles from '../../assets/jss/material-dashboard-react/components/cardHeaderStyle'; const useStyles = makeStyles(styles); @@ -28,14 +26,7 @@ export default function CardHeader(props) { CardHeader.propTypes = { className: PropTypes.string, - color: PropTypes.oneOf([ - 'warning', - 'success', - 'danger', - 'info', - 'primary', - 'rose', - ]), + color: PropTypes.oneOf(['warning', 'success', 'danger', 'info', 'primary', 'rose']), plain: PropTypes.bool, stats: PropTypes.bool, icon: PropTypes.bool, diff --git a/src/ui/components/Card/CardIcon.jsx b/src/ui/components/Card/CardIcon.jsx index 75af3bdb..f2b99469 100644 --- a/src/ui/components/Card/CardIcon.jsx +++ b/src/ui/components/Card/CardIcon.jsx @@ -1,10 +1,8 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import classNames from 'classnames'; import PropTypes from 'prop-types'; import { makeStyles } from '@material-ui/core/styles'; -import styles from '../../assets/jss/material-dashboard-react/components/cardIconStyle.js'; +import styles from '../../assets/jss/material-dashboard-react/components/cardIconStyle'; const useStyles = makeStyles(styles); @@ -25,13 +23,6 @@ export default function CardIcon(props) { CardIcon.propTypes = { className: PropTypes.string, - color: PropTypes.oneOf([ - 'warning', - 'success', - 'danger', - 'info', - 'primary', - 'rose', - ]), + color: PropTypes.oneOf(['warning', 'success', 'danger', 'info', 'primary', 'rose']), children: PropTypes.node, }; diff --git a/src/ui/components/CustomButtons/Button.jsx b/src/ui/components/CustomButtons/Button.jsx index 79144515..81090c19 100644 --- a/src/ui/components/CustomButtons/Button.jsx +++ b/src/ui/components/CustomButtons/Button.jsx @@ -1,11 +1,9 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import classNames from 'classnames'; import PropTypes from 'prop-types'; import { makeStyles } from '@material-ui/core/styles'; import Button from '@material-ui/core/Button'; -import styles from '../../assets/jss/material-dashboard-react/components/buttonStyle.js'; +import styles from '../../assets/jss/material-dashboard-react/components/buttonStyle'; const useStyles = makeStyles(styles); diff --git a/src/ui/components/CustomInput/CustomInput.jsx b/src/ui/components/CustomInput/CustomInput.jsx index f7d9d72d..b5eea683 100644 --- a/src/ui/components/CustomInput/CustomInput.jsx +++ b/src/ui/components/CustomInput/CustomInput.jsx @@ -1,5 +1,3 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import classNames from 'classnames'; import PropTypes from 'prop-types'; @@ -9,21 +7,13 @@ import InputLabel from '@material-ui/core/InputLabel'; import Input from '@material-ui/core/Input'; import Clear from '@material-ui/icons/Clear'; import Check from '@material-ui/icons/Check'; -import styles from '../../assets/jss/material-dashboard-react/components/customInputStyle.js'; +import styles from '../../assets/jss/material-dashboard-react/components/customInputStyle'; const useStyles = makeStyles(styles); export default function CustomInput(props) { const classes = useStyles(); - const { - formControlProps, - labelText, - id, - labelProps, - inputProps, - error, - success, - } = props; + const { formControlProps, labelText, id, labelProps, inputProps, error, success } = props; const labelClasses = classNames({ [' ' + classes.labelRootError]: error, @@ -37,17 +27,24 @@ export default function CustomInput(props) { const marginTop = classNames({ [classes.marginTop]: labelText === undefined, }); + + const generateIcon = () => { + if (error) { + return ; + } + if (success) { + return ; + } + return null; + }; + return ( {labelText !== undefined ? ( - + {labelText} ) : null} @@ -60,11 +57,7 @@ export default function CustomInput(props) { id={id} {...inputProps} /> - {error ? ( - - ) : success ? ( - - ) : null} + {generateIcon()} ); } diff --git a/src/ui/components/CustomTabs/CustomTabs.jsx b/src/ui/components/CustomTabs/CustomTabs.jsx index 481e2c67..ad125b00 100644 --- a/src/ui/components/CustomTabs/CustomTabs.jsx +++ b/src/ui/components/CustomTabs/CustomTabs.jsx @@ -1,14 +1,12 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import classNames from 'classnames'; import PropTypes from 'prop-types'; import { makeStyles } from '@material-ui/core/styles'; import Tabs from '@material-ui/core/Tabs'; import Tab from '@material-ui/core/Tab'; -import Card from '../Card/Card.jsx'; -import CardBody from '../Card/CardBody.jsx'; -import CardHeader from '../Card/CardHeader.jsx'; +import Card from '../Card/Card'; +import CardBody from '../Card/CardBody'; +import CardHeader from '../Card/CardHeader'; import styles from '../../assets/jss/material-dashboard-react/components/customTabsStyle'; @@ -16,8 +14,8 @@ const useStyles = makeStyles(styles); export default function CustomTabs(props) { const [value, setValue] = React.useState(0); - const handleChange = (event, value) => { - setValue(value); + const handleChange = (event, val) => { + setValue(val); }; const classes = useStyles(); const { headerColor, plainTabs, tabs, title, rtlActive } = props; @@ -37,8 +35,8 @@ export default function CustomTabs(props) { indicator: classes.displayNone, scrollButtons: classes.displayNone, }} - variant="scrollable" - scrollButtons="auto" + variant='scrollable' + scrollButtons='auto' > {tabs.map((prop, key) => { let icon = {}; @@ -75,14 +73,7 @@ export default function CustomTabs(props) { } CustomTabs.propTypes = { - headerColor: PropTypes.oneOf([ - 'warning', - 'success', - 'danger', - 'info', - 'primary', - 'rose', - ]), + headerColor: PropTypes.oneOf(['warning', 'success', 'danger', 'info', 'primary', 'rose']), title: PropTypes.string, tabs: PropTypes.arrayOf( PropTypes.shape({ diff --git a/src/ui/components/FixedPlugin/FixedPlugin.jsx b/src/ui/components/FixedPlugin/FixedPlugin.jsx deleted file mode 100644 index 78fcac38..00000000 --- a/src/ui/components/FixedPlugin/FixedPlugin.jsx +++ /dev/null @@ -1,189 +0,0 @@ -/*eslint-disable*/ -import React, { Component } from "react"; -// nodejs library to set properties for components -import PropTypes from "prop-types"; -// nodejs library that concatenates classes -import classnames from "classnames"; - -import imagine1 from "../../assets/img/sidebar-1.jpg"; -import imagine2 from "../../assets/img/sidebar-2.jpg"; -import imagine3 from "../../assets/img/sidebar-3.jpg"; -import imagine4 from "../../assets/img/sidebar-4.jpg"; -import Button from "../CustomButtons/Button.js"; - -export default function FixedPlugin(props) { - const [classes, setClasses] = React.useState("dropdown show"); - const [bg_checked, setBg_checked] = React.useState(true); - const [bgImage, setBgImage] = React.useState(props.bgImage); - const handleClick = () => { - props.handleFixedClick(); - }; - return ( - - ); -} - -FixedPlugin.propTypes = { - bgImage: PropTypes.string, - handleFixedClick: PropTypes.func, - rtlActive: PropTypes.bool, - fixedClasses: PropTypes.string, - bgColor: PropTypes.oneOf(["purple", "blue", "green", "orange", "red"]), - handleColorClick: PropTypes.func, - handleImageClick: PropTypes.func -}; diff --git a/src/ui/components/Footer/Footer.jsx b/src/ui/components/Footer/Footer.jsx index 4adf5519..6091f71b 100644 --- a/src/ui/components/Footer/Footer.jsx +++ b/src/ui/components/Footer/Footer.jsx @@ -1,10 +1,9 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import { makeStyles } from '@material-ui/core/styles'; import ListItem from '@material-ui/core/ListItem'; import List from '@material-ui/core/List'; -import styles from '../../assets/jss/material-dashboard-react/components/footerStyle.js'; +import styles from '../../assets/jss/material-dashboard-react/components/footerStyle'; +import { MarkGithubIcon } from '@primer/octicons-react'; const useStyles = makeStyles(styles); @@ -17,12 +16,12 @@ export default function Footer(props) { - Github + diff --git a/src/ui/components/Grid/GridContainer.jsx b/src/ui/components/Grid/GridContainer.jsx index 762dc922..e67c15ae 100644 --- a/src/ui/components/Grid/GridContainer.jsx +++ b/src/ui/components/Grid/GridContainer.jsx @@ -1,5 +1,3 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import PropTypes from 'prop-types'; import { makeStyles } from '@material-ui/core/styles'; diff --git a/src/ui/components/Grid/GridItem.jsx b/src/ui/components/Grid/GridItem.jsx index c4cbeecb..29cc2b26 100644 --- a/src/ui/components/Grid/GridItem.jsx +++ b/src/ui/components/Grid/GridItem.jsx @@ -1,5 +1,3 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import PropTypes from 'prop-types'; import { makeStyles } from '@material-ui/core/styles'; diff --git a/src/ui/components/Navbars/AdminNavbarLinks.jsx b/src/ui/components/Navbars/AdminNavbarLinks.jsx index 14a9ce36..1821f52c 100644 --- a/src/ui/components/Navbars/AdminNavbarLinks.jsx +++ b/src/ui/components/Navbars/AdminNavbarLinks.jsx @@ -1,6 +1,4 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ -import React from 'react'; +import React, { useEffect } from 'react'; import classNames from 'classnames'; import { makeStyles } from '@material-ui/core/styles'; import MenuItem from '@material-ui/core/MenuItem'; @@ -11,27 +9,29 @@ import ClickAwayListener from '@material-ui/core/ClickAwayListener'; import Hidden from '@material-ui/core/Hidden'; import Poppers from '@material-ui/core/Popper'; import Divider from '@material-ui/core/Divider'; -import Person from '@material-ui/icons/Person'; -import Notifications from '@material-ui/icons/Notifications'; -import Button from '../CustomButtons/Button.jsx'; -import styles from '../../assets/jss/material-dashboard-react/components/headerLinksStyle.js'; +import Button from '../CustomButtons/Button'; +import styles from '../../assets/jss/material-dashboard-react/components/headerLinksStyle'; +import { useNavigate } from 'react-router-dom'; +import { AccountCircle } from '@material-ui/icons'; +import { getUser } from '../../services/user'; +import axios from 'axios'; +import { getCookie } from '../../utils'; const useStyles = makeStyles(styles); export default function AdminNavbarLinks() { const classes = useStyles(); - const [openNotification, setOpenNotification] = React.useState(null); + const navigate = useNavigate(); const [openProfile, setOpenProfile] = React.useState(null); - const handleClickNotification = (event) => { - if (openNotification && openNotification.contains(event.target)) { - setOpenNotification(null); - } else { - setOpenNotification(event.currentTarget); - } - }; - const handleCloseNotification = () => { - setOpenNotification(null); - }; + const [, setAuth] = React.useState(true); + const [, setIsLoading] = React.useState(true); + const [, setIsError] = React.useState(false); + const [data, setData] = React.useState(false); + + useEffect(() => { + getUser(setIsLoading, setData, setAuth, setIsError); + }, []); + const handleClickProfile = (event) => { if (openProfile && openProfile.contains(event.target)) { setOpenProfile(null); @@ -42,98 +42,45 @@ export default function AdminNavbarLinks() { const handleCloseProfile = () => { setOpenProfile(null); }; + + const showProfile = () => { + navigate('/admin/profile', { replace: true }); + }; + + const logout = () => { + axios + .post( + `${import.meta.env.VITE_API_URI}/api/auth/logout`, + {}, + { + withCredentials: true, + headers: { + 'X-CSRF-TOKEN': getCookie('csrf'), + }, + }, + ) + .then((res) => { + if (!res.data.isAuth && !res.data.user) { + setAuth(false); + navigate(0); + } + }); + }; + return (
- - {({ TransitionProps, placement }) => ( - - - - - - Mike John responded to your email - - - You have 5 new tasks - - - ${`You're now friend with Andrew`} - - - Another Notification - - - Another One - - - - - - )} - -
-
- @@ -142,43 +89,28 @@ export default function AdminNavbarLinks() { anchorEl={openProfile} transition disablePortal - className={ - classNames({ [classes.popperClose]: !openProfile }) + - ' ' + - classes.popperNav - } + className={classNames({ [classes.popperClose]: !openProfile }) + ' ' + classes.popperNav} > {({ TransitionProps, placement }) => ( - - - Profile - - - Settings - - - - Logout + + + {data ? 'My Account' : 'Login'} + {!!data && } + {!!data && ( + + Logout + + )} diff --git a/src/ui/components/Navbars/Navbar.jsx b/src/ui/components/Navbars/Navbar.jsx index c12db17b..e3925bc8 100644 --- a/src/ui/components/Navbars/Navbar.jsx +++ b/src/ui/components/Navbars/Navbar.jsx @@ -1,5 +1,3 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import classNames from 'classnames'; import PropTypes from 'prop-types'; @@ -9,9 +7,8 @@ import Toolbar from '@material-ui/core/Toolbar'; import IconButton from '@material-ui/core/IconButton'; import Hidden from '@material-ui/core/Hidden'; import Menu from '@material-ui/icons/Menu'; -import AdminNavbarLinks from './AdminNavbarLinks.jsx'; -import Button from '../CustomButtons/Button.jsx'; -import styles from '../../assets/jss/material-dashboard-react/components/headerStyle.js'; +import AdminNavbarLinks from './AdminNavbarLinks'; +import styles from '../../assets/jss/material-dashboard-react/components/headerStyle'; const useStyles = makeStyles(styles); @@ -32,23 +29,23 @@ export default function Header(props) { [' ' + classes[color]]: color, }); return ( - +
{/* Here we create navbar brand, based on route name */} - +
- + - - + + diff --git a/src/ui/components/Sidebar/Sidebar.jsx b/src/ui/components/Sidebar/Sidebar.jsx index e21a42c9..174e31a4 100644 --- a/src/ui/components/Sidebar/Sidebar.jsx +++ b/src/ui/components/Sidebar/Sidebar.jsx @@ -1,5 +1,3 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import classNames from 'classnames'; import PropTypes from 'prop-types'; @@ -11,8 +9,7 @@ import List from '@material-ui/core/List'; import ListItem from '@material-ui/core/ListItem'; import ListItemText from '@material-ui/core/ListItemText'; import Icon from '@material-ui/core/Icon'; -import AdminNavbarLinks from '../Navbars/AdminNavbarLinks.jsx'; -import styles from '../../assets/jss/material-dashboard-react/components/sidebarStyle.js'; +import styles from '../../assets/jss/material-dashboard-react/components/sidebarStyle'; const useStyles = makeStyles(styles); @@ -22,8 +19,7 @@ export default function Sidebar(props) { function activeRoute(routeName) { return window.location.href.indexOf(routeName) > -1 ? true : false; } - // eslint-disable-next-line react/prop-types - const { color, logo, image, logoText, routes } = props; + const { color, logo, routes, background } = props; const links = ( {routes.map((prop, key) => { @@ -43,8 +39,8 @@ export default function Sidebar(props) { {typeof prop.icon === 'string' ? ( @@ -77,25 +73,23 @@ export default function Sidebar(props) { ); const brand = (
- - ); return ( -
- +
+ {brand} -
- - {links} -
- {image !== undefined ? ( -
- ) : null} +
{links}
+
- + {brand}
{links}
- {image !== undefined ? ( -
- ) : null} +
@@ -152,7 +133,6 @@ Sidebar.propTypes = { bgColor: PropTypes.oneOf(['purple', 'blue', 'green', 'orange', 'red']), logo: PropTypes.string, image: PropTypes.string, - logoText: PropTypes.string, routes: PropTypes.arrayOf(PropTypes.object), open: PropTypes.bool, }; diff --git a/src/ui/components/Snackbar/Snackbar.jsx b/src/ui/components/Snackbar/Snackbar.jsx index 1818ef98..2d09372a 100644 --- a/src/ui/components/Snackbar/Snackbar.jsx +++ b/src/ui/components/Snackbar/Snackbar.jsx @@ -1,5 +1,3 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import classNames from 'classnames'; import PropTypes from 'prop-types'; @@ -7,7 +5,7 @@ import { makeStyles } from '@material-ui/core/styles'; import Snack from '@material-ui/core/Snackbar'; import IconButton from '@material-ui/core/IconButton'; import Close from '@material-ui/icons/Close'; -import styles from '../../assets/jss/material-dashboard-react/components/snackbarContentStyle.js'; +import styles from '../../assets/jss/material-dashboard-react/components/snackbarContentStyle'; const useStyles = makeStyles(styles); @@ -22,25 +20,30 @@ export default function Snackbar(props) { action = [ props.closeNotification()} > , ]; } + + const calculateHorizontal = () => { + if (place.indexOf('l') !== -1) { + return 'left'; + } else if (place.indexOf('c') !== -1) { + return 'center'; + } + return 'right'; + }; + return ( + , ]; diff --git a/src/ui/components/Table/Table.jsx b/src/ui/components/Table/Table.jsx index 678f2943..e754a090 100644 --- a/src/ui/components/Table/Table.jsx +++ b/src/ui/components/Table/Table.jsx @@ -1,5 +1,3 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import PropTypes from 'prop-types'; import { makeStyles } from '@material-ui/core/styles'; @@ -9,7 +7,7 @@ import TableHead from '@material-ui/core/TableHead'; import TableRow from '@material-ui/core/TableRow'; import TableBody from '@material-ui/core/TableBody'; import TableCell from '@material-ui/core/TableCell'; -import styles from '../../assets/jss/material-dashboard-react/components/tableStyle.js'; +import styles from '../../assets/jss/material-dashboard-react/components/tableStyle'; const useStyles = makeStyles(styles); @@ -24,10 +22,7 @@ export default function CustomTable(props) { {tableHead.map((prop, key) => { return ( - + {prop} ); @@ -39,10 +34,10 @@ export default function CustomTable(props) { {tableData.map((prop, key) => { return ( - {prop.map((prop, key) => { + {prop.map((p, k) => { return ( - - {prop} + + {p} ); })} diff --git a/src/ui/components/Tasks/Tasks.jsx b/src/ui/components/Tasks/Tasks.jsx index cf6c2a18..84951c26 100644 --- a/src/ui/components/Tasks/Tasks.jsx +++ b/src/ui/components/Tasks/Tasks.jsx @@ -1,5 +1,3 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; @@ -14,7 +12,7 @@ import TableCell from '@material-ui/core/TableCell'; import Edit from '@material-ui/icons/Edit'; import Close from '@material-ui/icons/Close'; import Check from '@material-ui/icons/Check'; -import styles from '../../assets/jss/material-dashboard-react/components/tasksStyle.js'; +import styles from '../../assets/jss/material-dashboard-react/components/tasksStyle'; const useStyles = makeStyles(styles); @@ -56,37 +54,23 @@ export default function Tasks(props) { {tasks[value]} - - + + - - + + diff --git a/src/ui/components/Typography/Danger.jsx b/src/ui/components/Typography/Danger.jsx index 28368140..aee27132 100644 --- a/src/ui/components/Typography/Danger.jsx +++ b/src/ui/components/Typography/Danger.jsx @@ -1,20 +1,14 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import PropTypes from 'prop-types'; import { makeStyles } from '@material-ui/core/styles'; -import styles from '../../assets/jss/material-dashboard-react/components/typographyStyle.js'; +import styles from '../../assets/jss/material-dashboard-react/components/typographyStyle'; const useStyles = makeStyles(styles); export default function Danger(props) { const classes = useStyles(); const { children } = props; - return ( -
- {children} -
- ); + return
{children}
; } Danger.propTypes = { diff --git a/src/ui/components/Typography/Info.jsx b/src/ui/components/Typography/Info.jsx index 89594f27..f8aeebac 100644 --- a/src/ui/components/Typography/Info.jsx +++ b/src/ui/components/Typography/Info.jsx @@ -1,20 +1,14 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import PropTypes from 'prop-types'; import { makeStyles } from '@material-ui/core/styles'; -import styles from '../../assets/jss/material-dashboard-react/components/typographyStyle.js'; +import styles from '../../assets/jss/material-dashboard-react/components/typographyStyle'; const useStyles = makeStyles(styles); export default function Info(props) { const classes = useStyles(); const { children } = props; - return ( -
- {children} -
- ); + return
{children}
; } Info.propTypes = { diff --git a/src/ui/components/Typography/Muted.jsx b/src/ui/components/Typography/Muted.jsx index ffa829e5..68654797 100644 --- a/src/ui/components/Typography/Muted.jsx +++ b/src/ui/components/Typography/Muted.jsx @@ -1,22 +1,16 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import PropTypes from 'prop-types'; // @material-ui/core components import { makeStyles } from '@material-ui/core/styles'; // core components -import styles from '../../assets/jss/material-dashboard-react/components/typographyStyle.js'; +import styles from '../../assets/jss/material-dashboard-react/components/typographyStyle'; const useStyles = makeStyles(styles); export default function Muted(props) { const classes = useStyles(); const { children } = props; - return ( -
- {children} -
- ); + return
{children}
; } Muted.propTypes = { diff --git a/src/ui/components/Typography/Primary.jsx b/src/ui/components/Typography/Primary.jsx index 4c7679a2..38f3d81e 100644 --- a/src/ui/components/Typography/Primary.jsx +++ b/src/ui/components/Typography/Primary.jsx @@ -1,22 +1,16 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import PropTypes from 'prop-types'; // @material-ui/core components import { makeStyles } from '@material-ui/core/styles'; // core components -import styles from '../../assets/jss/material-dashboard-react/components/typographyStyle.js'; +import styles from '../../assets/jss/material-dashboard-react/components/typographyStyle'; const useStyles = makeStyles(styles); export default function Primary(props) { const classes = useStyles(); const { children } = props; - return ( -
- {children} -
- ); + return
{children}
; } Primary.propTypes = { diff --git a/src/ui/components/Typography/Quote.jsx b/src/ui/components/Typography/Quote.jsx index af4d3475..5ade54e4 100644 --- a/src/ui/components/Typography/Quote.jsx +++ b/src/ui/components/Typography/Quote.jsx @@ -1,11 +1,9 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import PropTypes from 'prop-types'; // @material-ui/core components import { makeStyles } from '@material-ui/core/styles'; // core components -import styles from '../../assets/jss/material-dashboard-react/components/typographyStyle.js'; +import styles from '../../assets/jss/material-dashboard-react/components/typographyStyle'; const useStyles = makeStyles(styles); diff --git a/src/ui/components/Typography/Success.jsx b/src/ui/components/Typography/Success.jsx index 23be56ab..56951ce6 100644 --- a/src/ui/components/Typography/Success.jsx +++ b/src/ui/components/Typography/Success.jsx @@ -1,22 +1,16 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import PropTypes from 'prop-types'; // @material-ui/core components import { makeStyles } from '@material-ui/core/styles'; // core components -import styles from '../../assets/jss/material-dashboard-react/components/typographyStyle.js'; +import styles from '../../assets/jss/material-dashboard-react/components/typographyStyle'; const useStyles = makeStyles(styles); export default function Success(props) { const classes = useStyles(); const { children } = props; - return ( -
- {children} -
- ); + return
{children}
; } Success.propTypes = { diff --git a/src/ui/components/Typography/Warning.jsx b/src/ui/components/Typography/Warning.jsx index 4a3f2a74..b8349411 100644 --- a/src/ui/components/Typography/Warning.jsx +++ b/src/ui/components/Typography/Warning.jsx @@ -1,22 +1,16 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; import PropTypes from 'prop-types'; // @material-ui/core components import { makeStyles } from '@material-ui/core/styles'; // core components -import styles from 'ui/assets/jss/material-dashboard-react/components/typographyStyle.js'; +import styles from 'ui/assets/jss/material-dashboard-react/components/typographyStyle'; const useStyles = makeStyles(styles); export default function Warning(props) { const classes = useStyles(); const { children } = props; - return ( -
- {children} -
- ); + return
{children}
; } Warning.propTypes = { diff --git a/src/ui/layouts/Admin.jsx b/src/ui/layouts/Admin.jsx index 862b091a..b08c6bab 100644 --- a/src/ui/layouts/Admin.jsx +++ b/src/ui/layouts/Admin.jsx @@ -1,33 +1,29 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ -import React from 'react'; -import { Routes, Route, Navigate } from 'react-router-dom'; +import React, { useState } from 'react'; +import { Routes, Route, Navigate, useParams } from 'react-router-dom'; +import PerfectScrollbar from 'perfect-scrollbar'; +import 'perfect-scrollbar/css/perfect-scrollbar.css'; import { makeStyles } from '@material-ui/core/styles'; -import Navbar from '../components/Navbars/Navbar.jsx'; -import Footer from '../components/Footer/Footer.jsx'; -import Sidebar from '../components/Sidebar/Sidebar.jsx'; -import routes from '../../routes.js'; -import styles from '../assets/jss/material-dashboard-react/layouts/adminStyle.js'; -import bgImage from '../assets/img/sidebar-2.jpg'; -import logo from '../assets/img/reactlogo.png'; +import Navbar from '../components/Navbars/Navbar'; +import Footer from '../components/Footer/Footer'; +import Sidebar from '../components/Sidebar/Sidebar'; +import routes from '../../routes'; +import styles from '../assets/jss/material-dashboard-react/layouts/adminStyle'; +import logo from '../assets/img/git-proxy.png'; +import { UserContext } from '../../context'; +import { getUser } from '../services/user'; let ps; +let refresh = false; const switchRoutes = ( - {routes - .filter((prop, _) => prop.layout === '/admin') - .map((prop, key) => { - return ( - } - /> - ); - })} - } /> + {routes.map((prop, key) => { + if (prop.layout === '/admin') { + return } key={key} />; + } + return null; + })} + } /> ); @@ -39,9 +35,9 @@ export default function Admin({ ...rest }) { // ref to help us initialize PerfectScrollbar on windows devices const mainPanel = React.createRef(); // states and functions - const [image] = React.useState(bgImage); const [color] = React.useState('blue'); const [mobileOpen, setMobileOpen] = React.useState(false); + const [user, setUser] = useState({}); const handleDrawerToggle = () => { setMobileOpen(!mobileOpen); @@ -55,51 +51,62 @@ export default function Admin({ ...rest }) { } }; // initialize and destroy the PerfectScrollbar plugin + + const { id } = useParams(); React.useEffect(() => { - if (navigator.platform.indexOf('Win') > -1) { - ps = new PerfectScrollbar(mainPanel.current, { - suppressScrollX: true, - suppressScrollY: false, - }); - document.body.style.overflow = 'hidden'; + async function loadUser() { + if (navigator.platform.indexOf('Win') > -1) { + ps = new PerfectScrollbar(mainPanel.current, { + suppressScrollX: true, + suppressScrollY: false, + }); + document.body.style.overflow = 'hidden'; + if (!refresh) { + refresh = true; + await getUser(null, setUser, null, null, null); + } + } + window.addEventListener('resize', resizeFunction); + if (!refresh) { + refresh = true; + await getUser(null, setUser, null, null, null); + } } - window.addEventListener('resize', resizeFunction); + loadUser(); + // Specify how to clean up after this effect: return function cleanup() { if (navigator.platform.indexOf('Win') > -1) { - ps.destroy(); + ps && ps.destroy(); } window.removeEventListener('resize', resizeFunction); }; - }, [mainPanel]); + }, [id]); return ( -
- -
- +
+ - {/* On the /maps route we want the map to be on full screen - this is not possible if the content and conatiner classes are present because they have some paddings which would make the map smaller */} - {getRoute() ? ( -
-
{switchRoutes}
-
- ) : ( -
{switchRoutes}
- )} - {getRoute() ?
: null} +
+ + {/* On the /maps route we want the map to be on full screen - this is not possible if the content and conatiner classes are present because they have some paddings which would make the map smaller */} + {getRoute() ? ( +
+
{switchRoutes}
+
+ ) : ( +
{switchRoutes}
+ )} + {getRoute() ?
: null} +
-
+ ); } diff --git a/src/ui/services/config.js b/src/ui/services/config.js new file mode 100644 index 00000000..edebc6e1 --- /dev/null +++ b/src/ui/services/config.js @@ -0,0 +1,28 @@ +import axios from 'axios'; + +const baseUrl = import.meta.env.VITE_API_URI + ? `${import.meta.env.VITE_API_URI}/api/v1` + : `${location.origin}/api/v1`; + +const getAttestationConfig = async (setData) => { + const url = new URL(`${baseUrl}/config/attestation`); + await axios(url.toString()).then((response) => { + setData(response.data.questions); + }); +}; + +const getURLShortener = async (setData) => { + const url = new URL(`${baseUrl}/config/urlShortener`); + await axios(url.toString()).then((response) => { + setData(response.data); + }); +}; + +const getEmailContact = async (setData) => { + const url = new URL(`${baseUrl}/config/contactEmail`); + await axios(url.toString()).then((response) => { + setData(response.data); + }); +}; + +export { getAttestationConfig, getURLShortener, getEmailContact }; diff --git a/src/ui/services/git-push.js b/src/ui/services/git-push.js index 8cdace59..df8f6f35 100644 --- a/src/ui/services/git-push.js +++ b/src/ui/services/git-push.js @@ -1,15 +1,16 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import axios from 'axios'; -const { GIT_PROXY_UI_PORT: uiPort } = require('../../config/env').Vars; -const baseUrl = `http://localhost:${uiPort}/api/v1`; +import { getCookie } from '../utils.jsx'; + +const baseUrl = import.meta.env.VITE_API_URI + ? `${import.meta.env.VITE_API_URI}/api/v1` + : `${location.origin}/api/v1`; const config = { withCredentials: true, }; const getUser = async (setIsLoading, setData, setAuth, setIsError) => { - const url = new URL(`http://localhost:${uiPort}/auth/success`); + const url = new URL(`${location.origin}/api/auth/success`); await axios(url.toString(), config) .then((response) => { const data = response.data; @@ -24,8 +25,8 @@ const getUser = async (setIsLoading, setData, setAuth, setIsError) => { }; const getPush = async (id, setIsLoading, setData, setAuth, setIsError) => { - const url = new URL(`${baseUrl}/push/${id}`); - await axios(url.toString(), config) + const url = `${baseUrl}/push/${id}`; + await axios(url, config) .then((response) => { const data = response.data; data.diff = data.steps.find((x) => x.stepName === 'diff'); @@ -54,6 +55,7 @@ const getPushes = async ( const url = new URL(`${baseUrl}/push`); url.search = new URLSearchParams(query); + setIsLoading(true); await axios(url.toString(), { withCredentials: true }) .then((response) => { const data = response.data; @@ -71,39 +73,50 @@ const getPushes = async ( }); }; -const authorisePush = async (id, setAuth, setIsError) => { +const authorisePush = async (id, setMessage, setUserAllowedToApprove, attestation) => { const url = `${baseUrl}/push/${id}/authorise`; + let errorMsg = ''; + let isUserAllowedToApprove = true; await axios - .post(url, {}, { withCredentials: true }) - .then(() => {}) + .post( + url, + { + params: { + attestation, + }, + }, + { withCredentials: true, headers: { 'X-CSRF-TOKEN': getCookie('csrf') } }, + ) .catch((error) => { if (error.response && error.response.status === 401) { - setAuth(false); - } else { - setIsError(true); + errorMsg = 'You are not authorised to approve...'; + isUserAllowedToApprove = false; } }); + await setMessage(errorMsg); + await setUserAllowedToApprove(isUserAllowedToApprove); }; -const rejectPush = async (id, setAuth, setIsError) => { +const rejectPush = async (id, setMessage, setUserAllowedToReject) => { const url = `${baseUrl}/push/${id}/reject`; + let errorMsg = ''; + let isUserAllowedToReject = true; await axios - .post(url, {}, { withCredentials: true }) - .then(() => {}) + .post(url, {}, { withCredentials: true, headers: { 'X-CSRF-TOKEN': getCookie('csrf') } }) .catch((error) => { if (error.response && error.response.status === 401) { - setAuth(false); - } else { - setIsError(true); + errorMsg = 'You are not authorised to reject...'; + isUserAllowedToReject = false; } }); + await setMessage(errorMsg); + await setUserAllowedToReject(isUserAllowedToReject); }; const cancelPush = async (id, setAuth, setIsError) => { const url = `${baseUrl}/push/${id}/cancel`; await axios - .post(url, {}, { withCredentials: true }) - .then((response) => {}) + .post(url, {}, { withCredentials: true, headers: { 'X-CSRF-TOKEN': getCookie('csrf') } }) .catch((error) => { if (error.response && error.response.status === 401) { setAuth(false); diff --git a/src/ui/services/repo.js b/src/ui/services/repo.js index 71c861c9..2ac0bc98 100644 --- a/src/ui/services/repo.js +++ b/src/ui/services/repo.js @@ -1,23 +1,42 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import axios from 'axios'; -const { GIT_PROXY_UI_PORT: uiPort } = require('../../config/env').Vars; -const baseUrl = `http://localhost:${uiPort}/api/v1`; +import { getCookie } from '../utils.jsx'; + +const baseUrl = import.meta.env.VITE_API_URI + ? `${import.meta.env.VITE_API_URI}/api/v1` + : `${location.origin}/api/v1`; const config = { withCredentials: true, }; -const getRepos = async ( - setIsLoading, - setData, - setAuth, - setIsError, - query = {}, -) => { +const canAddUser = (repoName, user, action) => { + const url = new URL(`${baseUrl}/repo/${repoName}`); + return axios + .get(url.toString(), config) + .then((response) => { + const data = response.data; + if (action === 'authorise') { + return !data.users.canAuthorise.includes(user); + } else { + return !data.users.canPush.includes(user); + } + }) + .catch((error) => { + throw error; + }); +}; + +class DupUserValidationError extends Error { + constructor(message) { + super(message); + this.name = 'The user already has this role...'; + } +} + +const getRepos = async (setIsLoading, setData, setAuth, setIsError, query = {}) => { const url = new URL(`${baseUrl}/repo`); url.search = new URLSearchParams(query); - + setIsLoading(true); await axios(url.toString(), config) .then((response) => { const data = response.data; @@ -37,6 +56,7 @@ const getRepos = async ( const getRepo = async (setIsLoading, setData, setAuth, setIsError, id) => { const url = new URL(`${baseUrl}/repo/${id}`); + setIsLoading(true); await axios(url.toString(), config) .then((response) => { const data = response.data; @@ -54,28 +74,56 @@ const getRepo = async (setIsLoading, setData, setAuth, setIsError, id) => { }); }; +const addRepo = async (onClose, setError, data) => { + const url = new URL(`${baseUrl}/repo`); + axios + .post(url, data, { withCredentials: true, headers: { 'X-CSRF-TOKEN': getCookie('csrf') } }) + .then(() => { + onClose(); + }) + .catch((error) => { + console.log(error.response.data.message); + setError(error.response.data.message); + }); +}; + const addUser = async (repoName, user, action) => { - const url = new URL(`${baseUrl}/repo/${repoName}/user/${action}`); - const data = { username: user }; + const canAdd = await canAddUser(repoName, user, action); + if (canAdd) { + const url = new URL(`${baseUrl}/repo/${repoName}/user/${action}`); + const data = { username: user }; + await axios + .patch(url, data, { withCredentials: true, headers: { 'X-CSRF-TOKEN': getCookie('csrf') } }) + .catch((error) => { + console.log(error.response.data.message); + throw error; + }); + } else { + console.log('Duplicate user can not be added'); + throw new DupUserValidationError(); + } +}; + +const deleteUser = async (user, repoName, action) => { + const url = new URL(`${baseUrl}/repo/${repoName}/user/${action}/${user}`); + await axios - .patch(url, data, { withCredentials: true }) - .then(() => {}) + .delete(url, { withCredentials: true, headers: { 'X-CSRF-TOKEN': getCookie('csrf') } }) .catch((error) => { console.log(error.response.data.message); throw error; }); }; -const deleteUser = async (user, repoName, action) => { - const url = new URL(`${baseUrl}/repo/${repoName}/user/${action}/${user}`); +const deleteRepo = async (repoName) => { + const url = new URL(`${baseUrl}/repo/${repoName}/delete`); await axios - .delete(url, { withCredentials: true }) - .then(() => {}) + .delete(url, { withCredentials: true, headers: { 'X-CSRF-TOKEN': getCookie('csrf') } }) .catch((error) => { console.log(error.response.data.message); throw error; }); }; -export { addUser, deleteUser, getRepos, getRepo }; +export { addUser, deleteUser, getRepos, getRepo, addRepo, deleteRepo }; diff --git a/src/ui/services/user.js b/src/ui/services/user.js index 35e8fda2..04a2fdcc 100644 --- a/src/ui/services/user.js +++ b/src/ui/services/user.js @@ -1,21 +1,16 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import axios from 'axios'; -const { GIT_PROXY_UI_PORT: uiPort } = require('../../config/env').Vars; -const baseUrl = `http://localhost:${uiPort}/api/v1`; +import { getCookie } from '../utils.jsx'; + +const baseUrl = import.meta.env.VITE_API_URI + ? `${import.meta.env.VITE_API_URI}` + : `${location.origin}`; const config = { withCredentials: true, }; -const getUser = async ( - setIsLoading, - setData, - setAuth, - setIsError, - id = null, -) => { - let url = `${baseUrl}/auth/profile`; +const getUser = async (setIsLoading, setData, setAuth, setIsError, id = null) => { + let url = `${baseUrl}/api/auth/profile`; if (id) { url = `${baseUrl}/api/v1/user/${id}`; @@ -24,46 +19,71 @@ const getUser = async ( console.log(url); await axios(url, config) + .then((response) => { + const data = response.data; + if (setData) { + setData(data); + } + if (setIsLoading) { + setIsLoading(false); + } + }) + .catch((error) => { + if (error.response && error.response.status === 401) { + if (setAuth) { + setAuth(false); + } + } else { + if (setIsError) { + setIsError(true); + } + } + if (setIsLoading) { + setIsLoading(false); + } + }); +}; + +const getUsers = async (setIsLoading, setData, setAuth, setIsError, query = {}) => { + const url = new URL(`${baseUrl}/api/v1/user`); + url.search = new URLSearchParams(query); + setIsLoading(true); + await axios(url.toString(), { withCredentials: true }) .then((response) => { const data = response.data; setData(data); - console.log(data); setIsLoading(false); }) .catch((error) => { - if (error.response && error.response.status === 401) setAuth(false); - else setIsError(true); + setIsLoading(false); + if (error.response && error.response.status === 401) { + setAuth(false); + } else { + setIsError(true); + } setIsLoading(false); }); }; -const createUser = async (data) => { +const updateUser = async (data) => { console.log(data); - const url = new URL(`${baseUrl}/auth/profile`); - await await axios - .post(url, data, { withCredentials: true }) - .then(() => {}) + const url = new URL(`${baseUrl}/api/auth/gitAccount`); + await axios + .post(url, data, { withCredentials: true, headers: { 'X-CSRF-TOKEN': getCookie('csrf') } }) .catch((error) => { console.log(error.response.data.message); throw error; }); }; -const getUsers = async ( - setIsLoading, - setData, - setAuth, - setIsError, - query = {}, -) => { - const url = new URL(`${baseUrl}/api/v1/user`); - url.search = new URLSearchParams(query); +const getUserLoggedIn = async (setIsLoading, setIsAdmin, setIsError, setAuth) => { + const url = new URL(`${baseUrl}/api/auth/userLoggedIn`); await axios(url.toString(), { withCredentials: true }) .then((response) => { const data = response.data; - setData(data); setIsLoading(false); + setIsAdmin(data.admin); }) .catch((error) => { setIsLoading(false); @@ -76,4 +96,4 @@ const getUsers = async ( }); }; -export { getUser, createUser, getUsers }; +export { getUser, getUsers, updateUser, getUserLoggedIn }; diff --git a/src/ui/utils.jsx b/src/ui/utils.jsx new file mode 100644 index 00000000..48a37f7f --- /dev/null +++ b/src/ui/utils.jsx @@ -0,0 +1,17 @@ +/** + * Retrieve a decoded cookie value from `document.cookie` with given `name`. + * @param {string} name + * @return {string} + */ +export const getCookie = (name) => { + if (!document.cookie) return null; + + const cookies = document.cookie + .split(';') + .map((c) => c.trim()) + .filter((c) => c.startsWith(name + '=')); + + if (!cookies.length) return null; + + return decodeURIComponent(cookies[0].split('=')[1]); +}; diff --git a/src/ui/variables/charts.js b/src/ui/variables/charts.js deleted file mode 100644 index b9e4247b..00000000 --- a/src/ui/variables/charts.js +++ /dev/null @@ -1,186 +0,0 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ -// ############################## -// // // javascript library for creating charts -// ############################# -import Chartist from 'chartist'; - -// ############################## -// // // variables used to create animation on charts -// ############################# -const delays = 80; -const durations = 500; -const delays2 = 80; -const durations2 = 500; - -// ############################## -// // // Daily Sales -// ############################# - -export const dailySalesChart = { - data: { - labels: ['M', 'T', 'W', 'T', 'F', 'S', 'S'], - series: [[12, 17, 7, 17, 23, 18, 38]], - }, - options: { - lineSmooth: Chartist.Interpolation.cardinal({ - tension: 0, - }), - low: 0, - high: 50, // creative tim: we recommend you to set the high sa the biggest value + something for a better look - chartPadding: { - top: 0, - right: 0, - bottom: 0, - left: 0, - }, - }, - // for animation - animation: { - draw: function (data) { - if (data.type === 'line' || data.type === 'area') { - data.element.animate({ - d: { - begin: 600, - dur: 700, - from: data.path - .clone() - .scale(1, 0) - .translate(0, data.chartRect.height()) - .stringify(), - to: data.path.clone().stringify(), - easing: Chartist.Svg.Easing.easeOutQuint, - }, - }); - } else if (data.type === 'point') { - data.element.animate({ - opacity: { - begin: (data.index + 1) * delays, - dur: durations, - from: 0, - to: 1, - easing: 'ease', - }, - }); - } - }, - }, -}; - -// ############################## -// // // Email Subscriptions -// ############################# - -export const emailsSubscriptionChart = { - data: { - labels: [ - 'Jan', - 'Feb', - 'Mar', - 'Apr', - 'Mai', - 'Jun', - 'Jul', - 'Aug', - 'Sep', - 'Oct', - 'Nov', - 'Dec', - ], - series: [[542, 443, 320, 780, 553, 453, 326, 434, 568, 610, 756, 895]], - }, - options: { - axisX: { - showGrid: false, - }, - low: 0, - high: 1000, - chartPadding: { - top: 0, - right: 5, - bottom: 0, - left: 0, - }, - }, - responsiveOptions: [ - [ - 'screen and (max-width: 640px)', - { - seriesBarDistance: 5, - axisX: { - labelInterpolationFnc: function (value) { - return value[0]; - }, - }, - }, - ], - ], - animation: { - draw: function (data) { - if (data.type === 'bar') { - data.element.animate({ - opacity: { - begin: (data.index + 1) * delays2, - dur: durations2, - from: 0, - to: 1, - easing: 'ease', - }, - }); - } - }, - }, -}; - -// ############################## -// // // Completed Tasks -// ############################# - -export const completedTasksChart = { - data: { - labels: ['12am', '3pm', '6pm', '9pm', '12pm', '3am', '6am', '9am'], - series: [[230, 750, 450, 300, 280, 240, 200, 190]], - }, - options: { - lineSmooth: Chartist.Interpolation.cardinal({ - tension: 0, - }), - low: 0, - high: 1000, // creative tim: we recommend you to set the high sa the biggest value + something for a better look - chartPadding: { - top: 0, - right: 0, - bottom: 0, - left: 0, - }, - }, - animation: { - draw: function (data) { - if (data.type === 'line' || data.type === 'area') { - data.element.animate({ - d: { - begin: 600, - dur: 700, - from: data.path - .clone() - .scale(1, 0) - .translate(0, data.chartRect.height()) - .stringify(), - to: data.path.clone().stringify(), - easing: Chartist.Svg.Easing.easeOutQuint, - }, - }); - } else if (data.type === 'point') { - data.element.animate({ - opacity: { - begin: (data.index + 1) * delays, - dur: durations, - from: 0, - to: 1, - easing: 'ease', - }, - }); - } - }, - }, -}; diff --git a/src/ui/variables/general.js b/src/ui/variables/general.js deleted file mode 100644 index 5ea360c2..00000000 --- a/src/ui/variables/general.js +++ /dev/null @@ -1,28 +0,0 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ -// ############################## -// // // Tasks for TasksCard - see Dashboard view -// ############################# - -const bugs = [ - 'Sign contract for "What are conference organizers afraid of?"', - 'Lines From Great Russian Literature? Or E-mails From My Boss?', - 'Flooded: One year later, assessing what was lost and what was found when a ravaging rain swept through metro Detroit', - 'Create 4 Invisible User Experiences you Never Knew About', -]; -const website = [ - 'Flooded: One year later, assessing what was lost and what was found when a ravaging rain swept through metro Detroit', - 'Sign contract for "What are conference organizers afraid of?"', -]; -const server = [ - 'Lines From Great Russian Literature? Or E-mails From My Boss?', - 'Flooded: One year later, assessing what was lost and what was found when a ravaging rain swept through metro Detroit', - 'Sign contract for "What are conference organizers afraid of?"', -]; - -module.exports = { - // these 3 are used to create the tasks lists in TasksCard - Dashboard view - bugs, - website, - server, -}; diff --git a/src/ui/views/Dashboard/Components/ContributorsTable.jsx b/src/ui/views/Dashboard/Components/ContributorsTable.jsx deleted file mode 100644 index b870261b..00000000 --- a/src/ui/views/Dashboard/Components/ContributorsTable.jsx +++ /dev/null @@ -1,44 +0,0 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ -import React from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import Table from '../../../components/Table/Table.jsx'; -import Card from '../../../components/Card/Card.jsx'; -import CardHeader from '../../../components/Card/CardHeader.jsx'; -import CardBody from '../../../components/Card/CardBody.jsx'; -import styles from '../../../assets/jss/material-dashboard-react/views/dashboardStyle.js'; - -const useStyles = makeStyles(styles); - -export default function ContributorsTable() { - const classes = useStyles(); - return ( - - -

Developer Stats

-

- New employees on 15th September, 2016 -

-
- - - - - ); -} diff --git a/src/ui/views/Dashboard/Components/DailyPullsGraph.jsx b/src/ui/views/Dashboard/Components/DailyPullsGraph.jsx deleted file mode 100644 index 8d9799ea..00000000 --- a/src/ui/views/Dashboard/Components/DailyPullsGraph.jsx +++ /dev/null @@ -1,47 +0,0 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ -import React from 'react'; -import ChartistGraph from 'react-chartist'; -import { makeStyles } from '@material-ui/core/styles'; -import AccessTime from '@material-ui/icons/AccessTime'; -import Card from '../../../components/Card/Card.jsx'; -import CardHeader from '../../../components/Card/CardHeader.jsx'; -import CardBody from '../../../components/Card/CardBody.jsx'; -import CardFooter from '../../../components/Card/CardFooter.jsx'; -import ArrowUpward from '@material-ui/icons/ArrowUpward'; -import styles from '../../../assets/jss/material-dashboard-react/views/dashboardStyle.js'; - -import { dailySalesChart } from '../../../variables/charts.js'; - -const useStyles = makeStyles(styles); - -export default function DailyPullsGraph() { - const classes = useStyles(); - return ( - - - - - -

Daily Github pulls

-

- - 55% - {' '} - increase in Git pulls today. -

-
- -
- updated 4 minutes ago -
-
-
- ); -} diff --git a/src/ui/views/Dashboard/Components/OpenPushRequestSummary.jsx b/src/ui/views/Dashboard/Components/OpenPushRequestSummary.jsx deleted file mode 100644 index b2947015..00000000 --- a/src/ui/views/Dashboard/Components/OpenPushRequestSummary.jsx +++ /dev/null @@ -1,37 +0,0 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ -import React from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import Icon from '@material-ui/core/Icon'; -import DateRange from '@material-ui/icons/DateRange'; -import Card from '../../../components/Card/Card.jsx'; -import CardHeader from '../../../components/Card/CardHeader.jsx'; -import CardIcon from '../../..//components/Card/CardIcon.jsx'; -import CardFooter from '../../../components/Card/CardFooter.jsx'; - -import styles from '../../../assets/jss/material-dashboard-react/views/dashboardStyle.js'; - -const useStyles = makeStyles(styles); - -export default function OpenPushRequestSummary() { - const classes = useStyles(); - return ( - - - - content_copy - -

Open Push requests

-

- 23 Pushes -

-
- -
- - 12 older than 1 day -
-
-
- ); -} diff --git a/src/ui/views/Dashboard/Components/PullRequestSummary.jsx b/src/ui/views/Dashboard/Components/PullRequestSummary.jsx deleted file mode 100644 index 7fdcba67..00000000 --- a/src/ui/views/Dashboard/Components/PullRequestSummary.jsx +++ /dev/null @@ -1,34 +0,0 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ -import React from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import Icon from '@material-ui/core/Icon'; -import LocalOffer from '@material-ui/icons/LocalOffer'; -import Card from '../../../components/Card/Card.jsx'; -import CardHeader from '../../../components/Card/CardHeader.jsx'; -import CardIcon from '../../../components/Card/CardIcon.jsx'; -import CardFooter from '../../../components/Card/CardFooter.jsx'; -import styles from '../../../assets/jss/material-dashboard-react/views/dashboardStyle.js'; - -const useStyles = makeStyles(styles); - -export default function PullRequestSummary() { - const classes = useStyles(); - return ( - - - - info_outline - -

Pull Requests

-

3275

-
- -
- - Pull requests from open-source repositories -
-
-
- ); -} diff --git a/src/ui/views/Dashboard/Components/PushAuthorizationsGraph.jsx b/src/ui/views/Dashboard/Components/PushAuthorizationsGraph.jsx deleted file mode 100644 index 37f8cc72..00000000 --- a/src/ui/views/Dashboard/Components/PushAuthorizationsGraph.jsx +++ /dev/null @@ -1,45 +0,0 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ -import React from 'react'; -import ChartistGraph from 'react-chartist'; -import { makeStyles } from '@material-ui/core/styles'; -import AccessTime from '@material-ui/icons/AccessTime'; -import Card from '../../../components/Card/Card.jsx'; -import CardHeader from '../../../components/Card/CardHeader.jsx'; -import CardBody from '../../../components/Card/CardBody.jsx'; -import CardFooter from '../../../components/Card/CardFooter.jsx'; - -import { emailsSubscriptionChart } from '../../../variables/charts.js'; - -import styles from '../../../assets/jss/material-dashboard-react/views/dashboardStyle.js'; - -const useStyles = makeStyles(styles); - -export default function PushAuthorizationsGraph() { - const classes = useStyles(); - return ( - - - - - -

Push Authorizations

-

- 20% increase in git-pushes waiting authorization -

-
- -
- updated 4 minutes ago -
-
-
- ); -} diff --git a/src/ui/views/Dashboard/Components/PushTable.jsx b/src/ui/views/Dashboard/Components/PushTable.jsx deleted file mode 100644 index 0f96cf70..00000000 --- a/src/ui/views/Dashboard/Components/PushTable.jsx +++ /dev/null @@ -1,99 +0,0 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ -import React, { useState, useEffect } from 'react'; -import axios from 'axios'; -import BugReport from '@material-ui/icons/BugReport'; -import Code from '@material-ui/icons/Code'; -import Table from '../../../components/Table/Table.jsx'; -import CustomTabs from '../../../components/CustomTabs/CustomTabs.jsx'; -import { Navigate } from 'react-router-dom'; - -export default function PushesWaitingAuthorizationGraph() { - const [data, setData] = useState([]); - const [auth, setAuth] = useState(true); - const url = 'http://localhost:8080/api/v1/push'; - const [isLoading, setIsLoading] = useState(false); - const [isError, setIsError] = useState(false); - - useEffect(() => { - const fetchData = async () => { - setIsError(false); - setIsLoading(true); - await axios(url, { withCredentials: true }) - .then((response) => { - const data = response.data.map((x) => [ - x.repo, - x.branch.replace('refs/heads/', ''), - x.commitFrom.substring(0, 8), - x.commitTo.substring(0, 8), - ]); - setData(data); - setAuth(true); - setIsLoading(false); - setIsError(false); - }) - .catch((error) => { - setIsLoading(false); - if (error.response && error.response.status === 401) { - setAuth(false); - } else { - setIsError(true); - } - }); - }; - - fetchData(); - }, [url]); - - if (isLoading) return
Loading ...
; - if (isError) return
Something went wrong ...
; - if (!auth) return ; - - return ( - Loading ... - ) : ( -
- ), - }, - { - tabName: 'Rejections', - tabIcon: BugReport, - tabContent: ( -
- ), - }, - ]} - /> - ); -} diff --git a/src/ui/views/Dashboard/Components/RejectedPushGraph.jsx b/src/ui/views/Dashboard/Components/RejectedPushGraph.jsx deleted file mode 100644 index c29f6bdd..00000000 --- a/src/ui/views/Dashboard/Components/RejectedPushGraph.jsx +++ /dev/null @@ -1,42 +0,0 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ -import React from 'react'; -import ChartistGraph from 'react-chartist'; -import { makeStyles } from '@material-ui/core/styles'; -import AccessTime from '@material-ui/icons/AccessTime'; -import Card from '../../../components/Card/Card.jsx'; -import CardHeader from '../../../components/Card/CardHeader.jsx'; -import CardBody from '../../../components/Card/CardBody.jsx'; -import CardFooter from '../../../components/Card/CardFooter.jsx'; - -import styles from '../../../assets/jss/material-dashboard-react/views/dashboardStyle.js'; - -import { completedTasksChart } from '../../../variables/charts.js'; - -const useStyles = makeStyles(styles); - -export default function DailyPullsGraph() { - const classes = useStyles(); - return ( - - - - - -

Push Rjections

-

10% reduction in push rejections

-
- -
- updated 4 minutes ago -
-
-
- ); -} diff --git a/src/ui/views/Dashboard/Components/RejectedPushRequestSummary.jsx b/src/ui/views/Dashboard/Components/RejectedPushRequestSummary.jsx deleted file mode 100644 index bd09efac..00000000 --- a/src/ui/views/Dashboard/Components/RejectedPushRequestSummary.jsx +++ /dev/null @@ -1,34 +0,0 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ -import React from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import Icon from '@material-ui/core/Icon'; -import LocalOffer from '@material-ui/icons/LocalOffer'; -import Card from '../../../components/Card/Card.jsx'; -import CardHeader from '../../../components/Card/CardHeader.jsx'; -import CardIcon from '../../../components/Card/CardIcon.jsx'; -import CardFooter from '../../../components/Card/CardFooter.jsx'; -import styles from '../../../assets/jss/material-dashboard-react/views/dashboardStyle.js'; - -const useStyles = makeStyles(styles); - -export default function RejectedPushRequestSummary() { - const classes = useStyles(); - return ( - - - - info_outline - -

Auto Rejected Pushes

-

75

-
- -
- - Due to hitting guardrails -
-
-
- ); -} diff --git a/src/ui/views/Dashboard/Components/SuccessfulPushRequestSummary.jsx b/src/ui/views/Dashboard/Components/SuccessfulPushRequestSummary.jsx deleted file mode 100644 index b5578f58..00000000 --- a/src/ui/views/Dashboard/Components/SuccessfulPushRequestSummary.jsx +++ /dev/null @@ -1,34 +0,0 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ -import React from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import Update from '@material-ui/icons/Update'; -import Accessibility from '@material-ui/icons/Accessibility'; -import Card from '../../../components/Card/Card.jsx'; -import CardHeader from '../../../components/Card/CardHeader.jsx'; -import CardIcon from '../../../components/Card/CardIcon.jsx'; -import CardFooter from '../../../components/Card/CardFooter.jsx'; -import styles from '../../../assets/jss/material-dashboard-react/views/dashboardStyle.js'; - -const useStyles = makeStyles(styles); - -export default function SuccessfulPushRequestSummary() { - const classes = useStyles(); - return ( - - - - - -

Sucessul Pushes

-

+245

-
- -
- - Last month -
-
-
- ); -} diff --git a/src/ui/views/Dashboard/Dashboard.jsx b/src/ui/views/Dashboard/Dashboard.jsx deleted file mode 100644 index 8ad2a2d1..00000000 --- a/src/ui/views/Dashboard/Dashboard.jsx +++ /dev/null @@ -1,54 +0,0 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ -import React from 'react'; -import GridItem from '../../components/Grid/GridItem.jsx'; -import GridContainer from '../../components/Grid/GridContainer.jsx'; -import OpenPushRequestSummary from './Components/OpenPushRequestSummary'; -import PullRequestSummary from './Components/PullRequestSummary'; -import RejectedPushRequestSummary from './Components/RejectedPushRequestSummary'; -import SuccessfulPushRequestSummary from './Components/SuccessfulPushRequestSummary'; -import DailyPullsGraph from './Components/DailyPullsGraph'; -import RejectedPushGraph from './Components/RejectedPushGraph'; -import PushAuthorizationsGraph from './Components/PushAuthorizationsGraph'; -import PushTable from './Components/PushTable'; -import ContributorsTable from './Components/ContributorsTable'; - -export default function Dashboard() { - return ( -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ); -} diff --git a/src/ui/views/Login/Login.jsx b/src/ui/views/Login/Login.jsx index 5c948c26..6d67f0ec 100644 --- a/src/ui/views/Login/Login.jsx +++ b/src/ui/views/Login/Login.jsx @@ -1,63 +1,43 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React, { useState } from 'react'; // @material-ui/core components -import { makeStyles } from '@material-ui/core/styles'; import FormControl from '@material-ui/core/FormControl'; import InputLabel from '@material-ui/core/InputLabel'; // core components -import GridItem from '../../components/Grid/GridItem.jsx'; -import GridContainer from '../../components/Grid/GridContainer.jsx'; +import GridItem from '../../components/Grid/GridItem'; +import GridContainer from '../../components/Grid/GridContainer'; import Input from '@material-ui/core/Input'; -import Button from '../../components/CustomButtons/Button.jsx'; -import Card from '../../components/Card/Card.jsx'; -import CardHeader from '../../components/Card/CardHeader.jsx'; -import CardBody from '../../components/Card/CardBody.jsx'; -import CardFooter from '../../components/Card/CardFooter.jsx'; +import Button from '../../components/CustomButtons/Button'; +import Card from '../../components/Card/Card'; +import CardHeader from '../../components/Card/CardHeader'; +import CardBody from '../../components/Card/CardBody'; +import CardFooter from '../../components/Card/CardFooter'; import axios from 'axios'; import { Navigate } from 'react-router-dom'; +import logo from '../../assets/img/git-proxy.png'; +import { Badge, CircularProgress, Snackbar } from '@material-ui/core'; +import { getCookie } from '../../utils'; -const styles = { - cardCategoryWhite: { - color: 'rgba(255,255,255,.62)', - margin: '0', - fontSize: '14px', - marginTop: '0', - marginBottom: '0', - }, - cardTitleWhite: { - color: '#FFFFFF', - marginTop: '0px', - minHeight: 'auto', - fontWeight: '300', - // eslint-disable-next-line quotes - fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", - marginBottom: '3px', - textDecoration: 'none', - }, -}; - -const { GIT_PROXY_UI_PORT: uiPort } = require('../../config/env').Vars; -const baseUrl = `http://localhost:${uiPort}`; - -const useStyles = makeStyles(styles); +const loginUrl = `${import.meta.env.VITE_API_URI}/api/auth/login`; export default function UserProfile() { - const classes = useStyles(); - const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [message, setMessage] = useState(''); const [success, setSuccess] = useState(false); + const [gitAccountError, setGitAccountError] = useState(false); + const [isLoading, setIsLoading] = useState(false); function validateForm() { - return username.length > 0 && password.length > 0; + return ( + username.length > 0 && username.length < 100 && password.length > 0 && password.length < 200 + ); } function handleSubmit(event) { + setIsLoading(true); axios .post( - `${baseUrl}/auth/login`, + loginUrl, { username: username, password: password, @@ -65,56 +45,90 @@ export default function UserProfile() { { withCredentials: true, headers: { - 'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json', + 'X-CSRF-TOKEN': getCookie('csrf'), }, }, ) - .then(function (response) { + .then(function () { + window.sessionStorage.setItem('git.proxy.login', 'success'); setMessage('Success!'); setSuccess(true); + setIsLoading(false); }) .catch(function (error) { - setMessage('Incorrect username of password'); + if (error.response.status === 307) { + window.sessionStorage.setItem('git.proxy.login', 'success'); + setGitAccountError(true); + } else if (error.response.status === 403) { + setMessage('You do not have the correct access permissions...'); + } else { + setMessage('You entered an invalid username or password...'); + } + setIsLoading(false); }); event.preventDefault(); } + if (gitAccountError) { + return ; + } if (success) { - return ; + return ; } return (
- - + setMessage('')} + /> + + - -

Login

-

{message}

+ +
+ logo +
- + Username setUsername(e.target.value)} + autoFocus={true} /> - + Password setPassword(e.target.value)} /> @@ -123,11 +137,24 @@ export default function UserProfile() { - + {!isLoading ? ( + + ) : ( +
+ +
+ )}
+
+ {' '} + + View our open source activity feed or{' '} + scroll through projects we contribute to + +
diff --git a/src/ui/views/OpenPushRequests/OpenPushRequests.jsx b/src/ui/views/OpenPushRequests/OpenPushRequests.jsx index eca1dfbf..f46f9671 100644 --- a/src/ui/views/OpenPushRequests/OpenPushRequests.jsx +++ b/src/ui/views/OpenPushRequests/OpenPushRequests.jsx @@ -1,22 +1,22 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; -import GridItem from '../../components/Grid/GridItem.jsx'; -import GridContainer from '../../components/Grid/GridContainer.jsx'; +import GridItem from '../../components/Grid/GridItem'; +import GridContainer from '../../components/Grid/GridContainer'; import PushesTable from './components/PushesTable'; import CustomTabs from '../../components/CustomTabs/CustomTabs'; +import { Visibility, CheckCircle, Cancel, Block } from '@material-ui/icons'; + export default function Dashboard() { return (
, }, { tabName: 'Canceled', - tabContent: ( - - ), + tabIcon: Cancel, + tabContent: , }, { tabName: 'Rejected', - // tabIcon: Code, - tabContent: ( - - ), + tabIcon: Block, + tabContent: , }, ]} /> diff --git a/src/ui/views/OpenPushRequests/components/PushesTable.jsx b/src/ui/views/OpenPushRequests/components/PushesTable.jsx index cb6f3dc5..a01f169e 100644 --- a/src/ui/views/OpenPushRequests/components/PushesTable.jsx +++ b/src/ui/views/OpenPushRequests/components/PushesTable.jsx @@ -1,5 +1,3 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React, { useState, useEffect } from 'react'; import { makeStyles } from '@material-ui/core/styles'; import moment from 'moment'; @@ -12,20 +10,20 @@ import TableContainer from '@material-ui/core/TableContainer'; import TableHead from '@material-ui/core/TableHead'; import TableRow from '@material-ui/core/TableRow'; import Paper from '@material-ui/core/Paper'; -import { Navigate } from 'react-router-dom'; -import styles from '../../../assets/jss/material-dashboard-react/views/dashboardStyle.js'; +import styles from '../../../assets/jss/material-dashboard-react/views/dashboardStyle'; import { getPushes } from '../../../services/git-push'; +import { KeyboardArrowRight } from '@material-ui/icons'; export default function PushesTable(props) { const useStyles = makeStyles(styles); const classes = useStyles(); const [data, setData] = useState([]); - const [auth, setAuth] = useState(true); + const [, setAuth] = useState(true); const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); - const history = useNavigate(); + const navigate = useNavigate(); - const openPush = (push) => history.push(`/admin/push/${push}`); + const openPush = (push) => navigate(`/admin/push/${push}`, { replace: true }); useEffect(() => { const query = {}; @@ -37,59 +35,102 @@ export default function PushesTable(props) { getPushes(setIsLoading, setData, setAuth, setIsError, query); }, [props]); - if (isLoading) return
Loading ...
; + if (isLoading) return
Loading...
; if (isError) return
Something went wrong ...
; - if (!auth) return ; return ( - -
- - - Actions - Time - Repo - Branch - Commit - Last Author - Last Message - Commits - - - - {data.map((row) => ( - - - - - - {moment(row.timestamp).format('yyyy-MM-DD HH:mm')} - - {row.repo} - - {row.branch.replace('refs/heads/', '')} - - - {row.commitFrom.substring(0, 5)} -{' '} - {row.commitTo.substring(0, 5)} - - - {row.commitData[row.commitData.length - 1].author}{' '} - - - {row.commitData[row.commitData.length - 1].message}{' '} - - {row.commitData.length} +
+ +
+ + + Timestamp + Repository + Branch + Commit SHA + Committer + Author + Author E-mail + Commit Message + No. of Commits + - ))} - -
- + + + {[...data].reverse().map((row) => { + const repoFullName = row.repo.replace('.git', ''); + const repoBranch = row.branch.replace('refs/heads/', ''); + + return ( + + + {moment + .unix(row.commitData[0].commitTs || row.commitData[0].commitTimestamp) + .toString()} + + + + {repoFullName} + + + + + {repoBranch} + + + + + {row.commitTo.substring(0, 8)} + + + + + {row.commitData[0].committer} + + + + + {row.commitData[0].author} + + + + {row.commitData[0].authorEmail ? ( + + {row.commitData[0].authorEmail} + + ) : ( + 'No data...' + )}{' '} + + {row.commitData[0].message} + {row.commitData.length} + + + + + ); + })} + + + +
); } diff --git a/src/ui/views/PushDetails/PushDetails.jsx b/src/ui/views/PushDetails/PushDetails.jsx index 3713d895..6f02d717 100644 --- a/src/ui/views/PushDetails/PushDetails.jsx +++ b/src/ui/views/PushDetails/PushDetails.jsx @@ -1,65 +1,74 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React, { useState, useEffect } from 'react'; -import { Navigate } from 'react-router-dom'; import moment from 'moment'; import { useNavigate, useParams } from 'react-router-dom'; import Icon from '@material-ui/core/Icon'; -import GridItem from '../../components/Grid/GridItem.jsx'; -import GridContainer from '../../components/Grid/GridContainer.jsx'; -import Card from '../../components/Card/Card.jsx'; -import CardIcon from '../../components/Card/CardIcon.jsx'; -import CardBody from '../../components/Card/CardBody.jsx'; -import CardHeader from '../../components/Card/CardHeader.jsx'; -import CardFooter from '../../components/Card/CardFooter.jsx'; -import Button from '../../components/CustomButtons/Button.jsx'; +import GridItem from '../../components/Grid/GridItem'; +import GridContainer from '../../components/Grid/GridContainer'; +import Card from '../../components/Card/Card'; +import CardIcon from '../../components/Card/CardIcon'; +import CardBody from '../../components/Card/CardBody'; +import CardHeader from '../../components/Card/CardHeader'; +import CardFooter from '../../components/Card/CardFooter'; +import Button from '../../components/CustomButtons/Button'; import Diff from './components/Diff'; +import Attestation from './components/Attestation'; +import AttestationView from './components/AttestationView'; import Table from '@material-ui/core/Table'; import TableBody from '@material-ui/core/TableBody'; import TableHead from '@material-ui/core/TableHead'; import TableRow from '@material-ui/core/TableRow'; import TableCell from '@material-ui/core/TableCell'; -import { - getPush, - authorisePush, - rejectPush, - cancelPush, -} from '../../services/git-push.js'; +import { getPush, authorisePush, rejectPush, cancelPush } from '../../services/git-push'; +import { CheckCircle, Visibility, Cancel, Block } from '@material-ui/icons'; +import Snackbar from '@material-ui/core/Snackbar'; +import Tooltip from '@material-ui/core/Tooltip'; export default function Dashboard() { - // eslint-disable-next-line react/prop-types const { id } = useParams(); const [data, setData] = useState([]); - const [auth, setAuth] = useState(true); + const [, setAuth] = useState(true); const [isLoading, setIsLoading] = useState(true); const [isError, setIsError] = useState(false); - const history = useNavigate(); - + const [message, setMessage] = useState(''); + const [attestation, setAttestation] = useState(false); + const navigate = useNavigate(); + let isUserAllowedToApprove = true; + let isUserAllowedToReject = true; + function setUserAllowedToApprove(userAllowedToApprove) { + isUserAllowedToApprove = userAllowedToApprove; + console.log('isUserAllowedToApprove:' + isUserAllowedToApprove); + } + function setUserAllowedToReject(userAllowedToReject) { + isUserAllowedToReject = userAllowedToReject; + console.log({ isUserAllowedToReject }); + } useEffect(() => { getPush(id, setIsLoading, setData, setAuth, setIsError); }, [id]); - - const authorise = async () => { - await authorisePush(id, setAuth, setIsError); - history.push(`/admin/push/`); + const authorise = async (attestationData) => { + await authorisePush(id, setMessage, setUserAllowedToApprove, attestationData); + if (isUserAllowedToApprove) { + navigate('/admin/push/'); + } }; const reject = async () => { - await rejectPush(id, setAuth, setIsError); - history.push(`/admin/push/`); + await rejectPush(id, setMessage, setUserAllowedToReject); + if (isUserAllowedToReject) { + navigate('/admin/push/'); + } }; const cancel = async () => { await cancelPush(id, setAuth, setIsError); - history.push(`/admin/push/`); + navigate(`/admin/push/`); }; - if (isLoading) return
Loading ...
; + if (isLoading) return
Loading...
; if (isError) return
Something went wrong ...
; - if (!auth) return ; let headerData = { - title: 'Waiting Approval', + title: 'Pending', color: 'warning', }; @@ -73,117 +82,250 @@ export default function Dashboard() { if (data.rejected) { headerData = { color: 'danger', - title: 'REJECTED', + title: 'Rejected', }; } if (data.authorised) { headerData = { color: 'success', - title: 'Approved!', + title: 'Approved', }; } + const repoFullName = data.repo.replace('.git', ''); + const repoBranch = data.branch.replace('refs/heads/', ''); + + const generateIcon = (title) => { + switch (title) { + case 'Approved': + return ; + case 'Pending': + return ; + case 'Canceled': + return ; + case 'Rejected': + return ; + default: + return ; + } + }; + return ( - - - - - - content_copy -

{headerData.title}

-
- - - -
- - - -

Timestamp

-

{moment(data.timestamp).toString()}

-
- -

Remote Head

-

{data.commitFrom}

-
- -

Commit

-

{data.commitTo}

-
- -

Repo

-

{data.repo}

-
- -

Branch

-

{data.branch.replace('refs/heads/', '')}

-
-
-
-
- - -

{headerData.title}

-
- - - - Parent# - Committer - Author - Timestamp - Message - - - {data.commitData.map((c) => ( - - {c.parent} - {c.committer} - {c.author} - - {moment(Date(c.commitTs)).format( - 'yyyy-MM-DD HH:mm:ss.mmmm', +
+ setMessage('')} + /> + + + + + + {generateIcon(headerData.title)} +

{headerData.title}

+
+ {!(data.canceled || data.rejected || data.authorised) ? ( +
+ + + +
+ ) : null} + {data.attestation && data.authorised ? ( +
+ + setAttestation(true)} + htmlColor='green' + /> + + + + +
+

+ + {data.attestation.reviewer.gitAccount} + {' '} + approved this contribution +

+ - {c.message} - - ))} - -
-
-
-
- - - - - - - - - -
+ arrow + > + + {moment(data.attestation.timestamp).fromNow()} + + +
+ +
+ ) : null} + + + + +

Timestamp

+

{moment(data.timestamp).toString()}

+
+ +

Remote Head

+

+ + {data.commitFrom} + +

+
+ +

Commit SHA

+

+ + {data.commitTo} + +

+
+ +

Repository

+

+ + {repoFullName} + +

+
+ +

Branch

+

+ + {repoBranch} + +

+
+
+
+ + + +

{headerData.title}

+
+ + + + Timestamp + Committer + Author + Author E-mail + Message + + + {data.commitData.map((c) => ( + + + {moment.unix(c.commitTs || c.commitTimestamp).toString()} + + + + {c.committer} + + + + + {c.author} + + + + {c.authorEmail ? ( + {c.authorEmail} + ) : ( + 'No data...' + )} + + {c.message} + + ))} + +
+
+
+ + + + + + + + + + + +
); } diff --git a/src/ui/views/PushDetails/components/Attestation.jsx b/src/ui/views/PushDetails/components/Attestation.jsx new file mode 100644 index 00000000..c13663ed --- /dev/null +++ b/src/ui/views/PushDetails/components/Attestation.jsx @@ -0,0 +1,106 @@ +import React, { useEffect } from 'react'; +import Dialog from '@material-ui/core/Dialog'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogActions from '@material-ui/core/DialogActions'; +import { CheckCircle, ErrorOutline } from '@material-ui/icons'; +import Button from '../../../components/CustomButtons/Button'; +import AttestationForm from './AttestationForm'; + +import { getAttestationConfig, getURLShortener, getEmailContact } from '../../../services/config'; + +export default function Attestation(props) { + const [open, setOpen] = React.useState(false); + const [formData, setFormData] = React.useState([]); + const [urlShortener, setURLShortener] = React.useState(''); + const [contactEmail, setContactEmail] = React.useState(''); + + useEffect(() => { + if (!open) { + getAttestationConfig(setFormData); + } + + if (open) { + if (!urlShortener) { + getURLShortener(setURLShortener); + } + if (!contactEmail) { + getEmailContact(setContactEmail); + } + } + }, [open]); + + const handleClickOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + const handleApprove = () => { + const data = formData.map((question) => { + return { + label: question.label, + checked: question.checked, + }; + }); + props.approveFn(data); + }; + + return ( +
+ + + +
+ + + You are about to approve a contribution for publication to GitHub + +
+

+ Feeling uneasy with approving this contribution? +
+ Review the company open source contribution policy or{' '} + contact the Open Source Program Office. +

+
+ +

By approving this contribution, I confirm that:

+ +
+ + + + +
+
+ ); +} diff --git a/src/ui/views/PushDetails/components/AttestationForm.jsx b/src/ui/views/PushDetails/components/AttestationForm.jsx new file mode 100644 index 00000000..d1c1d781 --- /dev/null +++ b/src/ui/views/PushDetails/components/AttestationForm.jsx @@ -0,0 +1,83 @@ +import React from 'react'; +import { withStyles } from '@material-ui/core/styles'; +import { green } from '@material-ui/core/colors'; +import { Help } from '@material-ui/icons'; +import { Grid, Tooltip, Checkbox, FormGroup, FormControlLabel } from '@material-ui/core'; + +const GreenCheckbox = withStyles({ + root: { + color: green[500], + '&$checked': { + color: green[700], + }, + paddingRight: '35px', + }, + checked: {}, +})((props) => ); + +const HTMLTooltip = withStyles((theme) => ({ + tooltip: { + backgroundColor: '#f5f5f9', + color: 'rgba(0, 0, 0, 0.87)', + maxWidth: 220, + fontSize: theme.typography.pxToRem(12), + border: '1px solid #dadde9', + }, +}))(Tooltip); + +export default function AttestationForm(props) { + const handleChange = (event) => { + const name = event.target.name; + const checked = event.target.checked; + const clone = [...props.formData]; + clone[name] = { ...clone[name], checked }; + props.passFormData(clone); + }; + + return ( + + {props.formData.map((question, index) => { + return ( + + + + } + label={question.label} + /> + + + + {question.tooltip.text} + {question.tooltip.links && ( +
+
    + {question.tooltip.links.map((link, linkIndex) => { + return ( +
  • + + {link.text} + +
  • + ); + })} +
+
+ )} + + } + > + +
+
+
+ ); + })} +
+ ); +} diff --git a/src/ui/views/PushDetails/components/AttestationView.jsx b/src/ui/views/PushDetails/components/AttestationView.jsx new file mode 100644 index 00000000..70540ca7 --- /dev/null +++ b/src/ui/views/PushDetails/components/AttestationView.jsx @@ -0,0 +1,124 @@ +import React, { useEffect } from 'react'; +import Dialog from '@material-ui/core/Dialog'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogActions from '@material-ui/core/DialogActions'; +import FormGroup from '@material-ui/core/FormGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import { CheckCircle } from '@material-ui/icons'; +import Tooltip from '@material-ui/core/Tooltip'; +import moment from 'moment'; + +import Checkbox from '@material-ui/core/Checkbox'; +import { withStyles } from '@material-ui/core/styles'; +import { green } from '@material-ui/core/colors'; + +import { getURLShortener } from '../../../services/config'; + +const GreenCheckbox = withStyles({ + root: { + color: green[500], + '&$checked': { + color: green[700], + }, + paddingRight: '35px', + }, + checked: {}, +})((props) => ); + +export default function AttestationView(props) { + const [urlShortener, setURLShortener] = React.useState(''); + + useEffect(() => { + if (props.attestation && !urlShortener) { + getURLShortener(setURLShortener); + } + }, [props.attestation]); + + return ( + props.setAttestation(false)} + aria-labelledby='alert-dialog-title' + aria-describedby='alert-dialog-description' + style={{ margin: '0px 15px 0px 15px' }} + > + +
+ + + What does it mean for a code contribution to be approved? + +
+

+ Prior to making this code contribution publicly accessible via GitHub, this code + contribution was reviewed and approved by{' '} + + {props.data.reviewer.gitAccount} + + . As a reviewer, it was their responsibility to confirm that open sourcing this + contribution followed the requirements of the company open source contribution policy. +

+
+ +

+ + + {props.data.reviewer.gitAccount} + {' '} + approved this contribution{' '} + + + {moment(props.data.timestamp).fromNow()} + + {' '} + and confirmed that: + +

+ + + {props.data.questions.map((question, index) => { + return ( +
+ } + disabled={true} + label={question.label} + /> +
+ ); + })} +
+
+ +
+ ); +} diff --git a/src/ui/views/PushDetails/components/Diff.js b/src/ui/views/PushDetails/components/Diff.jsx similarity index 82% rename from src/ui/views/PushDetails/components/Diff.js rename to src/ui/views/PushDetails/components/Diff.jsx index 60e979c3..44e60cd0 100644 --- a/src/ui/views/PushDetails/components/Diff.js +++ b/src/ui/views/PushDetails/components/Diff.jsx @@ -1,5 +1,3 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import * as Diff2Html from 'diff2html'; import ReactHtmlParser from 'react-html-parser'; diff --git a/src/ui/views/RepoDetails/Components/AddUser.jsx b/src/ui/views/RepoDetails/Components/AddUser.jsx index d69fc9bf..afab44a5 100644 --- a/src/ui/views/RepoDetails/Components/AddUser.jsx +++ b/src/ui/views/RepoDetails/Components/AddUser.jsx @@ -1,24 +1,22 @@ -/* eslint-disable react/prop-types */ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import InputLabel from '@material-ui/core/InputLabel'; import FormControl from '@material-ui/core/FormControl'; import FormHelperText from '@material-ui/core/FormHelperText'; -import GridItem from '../../../components/Grid/GridItem.jsx'; -import GridContainer from '../../../components/Grid/GridContainer.jsx'; -import Card from '../../../components/Card/Card.jsx'; -import CardBody from '../../../components/Card/CardBody.jsx'; +import GridItem from '../../../components/Grid/GridItem'; +import GridContainer from '../../../components/Grid/GridContainer'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import Card from '../../../components/Card/Card'; +import CardBody from '../../../components/Card/CardBody'; import MenuItem from '@material-ui/core/MenuItem'; -import Button from '../../../components/CustomButtons/Button.jsx'; +import Button from '../../../components/CustomButtons/Button'; import DialogTitle from '@material-ui/core/DialogTitle'; import Select from '@material-ui/core/Select'; import Dialog from '@material-ui/core/Dialog'; -import { Navigate } from 'react-router-dom'; - -import { addUser } from '../../../services/repo.js'; -import { getUsers } from '../../../services/user.js'; +import Snackbar from '@material-ui/core/Snackbar'; +import { addUser } from '../../../services/repo'; +import { getUsers } from '../../../services/user'; +import { PersonAdd } from '@material-ui/icons'; function AddUserDialog(props) { const repoName = props.repoName; @@ -26,26 +24,35 @@ function AddUserDialog(props) { const refreshFn = props.refreshFn; const [username, setUsername] = useState(''); const [data, setData] = useState([]); - const [auth, setAuth] = useState(true); + const [, setAuth] = useState(true); const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); const [error, setError] = useState(''); + const [tip, setTip] = useState(false); const { onClose, open } = props; const handleClose = () => { - refreshFn(); + setError(''); onClose(); }; + const handleSuccess = () => { + setTip(true); + refreshFn(); + }; + const handleChange = (event) => { setUsername(event.target.value); }; const add = async () => { try { + setIsLoading(true); await addUser(repoName, username, type); + handleSuccess(); handleClose(); } catch (e) { + setIsLoading(false); if (e.message) { setError(JSON.stringify(e)); } else { @@ -54,58 +61,78 @@ function AddUserDialog(props) { } }; + const inputStyle = { + width: '100%', + }; + useEffect(() => { getUsers(setIsLoading, setData, setAuth, setIsError, {}); }, [props]); - if (isLoading) return
Loading ...
; if (isError) return
Something went wrong ...
; - if (!auth) return ; - console.log(JSON.stringify(props)); + let spinner; + if (isLoading) { + spinner = ; + } return ( - - - {error} Add User to {repoName} for {type}{' '} - - - - - - - Username - - Some important helper text - - - - - - - - - - + <> + setTip(false)} + /> + + + Add a user...

{error}

{spinner} +
+ + + + + + Username + + + + + + + + + + + +
+ ); } @@ -131,9 +158,9 @@ export default function AddUser(props) { }; return ( -
- -
+ ); } diff --git a/src/ui/views/RepoDetails/RepoDetails.jsx b/src/ui/views/RepoDetails/RepoDetails.jsx index 2a7edbc0..0c5c9786 100644 --- a/src/ui/views/RepoDetails/RepoDetails.jsx +++ b/src/ui/views/RepoDetails/RepoDetails.jsx @@ -1,13 +1,9 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ -import React, { useState, useEffect } from 'react'; -import { Navigate } from 'react-router-dom'; -// import Icon from '@material-ui/core/Icon'; -import GridItem from '../../components/Grid/GridItem.jsx'; -import GridContainer from '../../components/Grid/GridContainer.jsx'; -import Card from '../../components/Card/Card.jsx'; -import CardBody from '../../components/Card/CardBody.jsx'; -import TextField from '@material-ui/core/TextField'; +import React, { useState, useEffect, useContext } from 'react'; +import GridItem from '../../components/Grid/GridItem'; +import GridContainer from '../../components/Grid/GridContainer'; +import Card from '../../components/Card/Card'; +import CardBody from '../../components/Card/CardBody'; +import FormLabel from '@material-ui/core/FormLabel'; import Paper from '@material-ui/core/Paper'; import Button from '@material-ui/core/Button'; import Table from '@material-ui/core/Table'; @@ -16,9 +12,12 @@ import TableCell from '@material-ui/core/TableCell'; import TableContainer from '@material-ui/core/TableContainer'; import TableHead from '@material-ui/core/TableHead'; import TableRow from '@material-ui/core/TableRow'; -import { getRepo, deleteUser } from '../../services/repo.js'; +import { getRepo, deleteUser, deleteRepo } from '../../services/repo'; import { makeStyles } from '@material-ui/core/styles'; import AddUser from './Components/AddUser'; +import { Code, Delete, RemoveCircle, Visibility } from '@material-ui/icons'; +import { useNavigate, useParams } from 'react-router-dom'; +import { UserContext } from '../../../context'; const useStyles = makeStyles((theme) => ({ root: { @@ -29,138 +28,181 @@ const useStyles = makeStyles((theme) => ({ }, })); -export default function RepoDetails(props) { +export default function RepoDetails() { + const navigate = useNavigate(); const classes = useStyles(); const [data, setData] = useState([]); - const [auth, setAuth] = useState(true); + const [, setAuth] = useState(true); const [isLoading, setIsLoading] = useState(true); const [isError, setIsError] = useState(false); - // eslint-disable-next-line react/prop-types - const repoName = props.match.params.id; + const { user } = useContext(UserContext); + const { id: repoName } = useParams(); useEffect(() => { - // eslint-disable-next-line react/prop-types - const id = props.match.params.id; - getRepo(setIsLoading, setData, setAuth, setIsError, id); - }, [props]); + getRepo(setIsLoading, setData, setAuth, setIsError, repoName); + }, []); - const removeUser = async (user, action) => { - await deleteUser(user, repoName, action); + const removeUser = async (userToRemove, action) => { + await deleteUser(userToRemove, repoName, action); getRepo(setIsLoading, setData, setAuth, setIsError, repoName); }; - const refresh = () => - getRepo(setIsLoading, setData, setAuth, setIsError, repoName); + const removeRepository = async (name) => { + await deleteRepo(name); + navigate('/admin/repo', { replace: true }); + }; + + const refresh = () => getRepo(setIsLoading, setData, setAuth, setIsError, repoName); - if (isLoading) return
Loading ...
; + if (isLoading) return
Loading...
; if (isError) return
Something went wrong ...
; - if (!auth) return ; return ( -
+ {user.admin && ( +
+ +
+ )} + - - + + - - + + Organization +

+ + {data.project} + +

- - + + Name +

+ + {data.name} + +

+
+ + URL +

+ + {data.url.replace('.git', '')} + +

+
- -

Can Authorise Push

- -
-
+ +

+ Reviewers +

+ {user.admin && ( +
+ +
+ )} - +
- Actions - Username + Username + {user.admin && } - {data.users.canAuthorise.map((row) => ( - - - - - {row} - - ))} + {data.users.canAuthorise.map((row) => { + if (row) + return ( + + + {row} + + {user.admin && ( + + + + )} + + ); + })}
- -

Can Push

- -
-
+
+ + +

+ Contributors +

+ {user.admin && ( +
+ +
+ )} - +
- Actions - Username + Username + {user.admin && } - {data.users.canPush.map((row) => ( - - - - - {row} - - ))} + {data.users.canPush.map((row) => { + if (row) { + return ( + + + {row} + + {user.admin && ( + + + + )} + + ); + } + })}
diff --git a/src/ui/views/RepoList/Components/NewRepo.jsx b/src/ui/views/RepoList/Components/NewRepo.jsx new file mode 100644 index 00000000..3aef1d38 --- /dev/null +++ b/src/ui/views/RepoList/Components/NewRepo.jsx @@ -0,0 +1,201 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; +import InputLabel from '@material-ui/core/InputLabel'; +import Input from '@material-ui/core/Input'; +import FormControl from '@material-ui/core/FormControl'; +import FormHelperText from '@material-ui/core/FormHelperText'; +import GridItem from '../../../components/Grid/GridItem'; +import GridContainer from '../../../components/Grid/GridContainer'; +import Card from '../../../components/Card/Card'; +import CardBody from '../../../components/Card/CardBody'; +import Button from '../../../components/CustomButtons/Button'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import Dialog from '@material-ui/core/Dialog'; +import Snackbar from '@material-ui/core/Snackbar'; +import { addRepo } from '../../../services/repo'; +import { makeStyles } from '@material-ui/core/styles'; +import styles from '../../../assets/jss/material-dashboard-react/views/dashboardStyle'; +import { RepoIcon } from '@primer/octicons-react'; + +const useStyles = makeStyles(styles); + +function AddRepositoryDialog(props) { + const [project, setProject] = useState(''); + const [name, setName] = useState(''); + const [url, setUrl] = useState(''); + const [error, setError] = useState(''); + const [tip, setTip] = useState(false); + const { onClose, open, onSuccess } = props; + const classes = useStyles(); + + const handleClose = () => { + setError(''); + resetRepo(); + onClose(); + }; + + const handleSuccess = (data) => { + onSuccess(data); + setTip(true); + }; + + const resetRepo = () => { + setProject(''); + setName(''); + setUrl(''); + }; + + const add = async () => { + const data = { + project: project, + name: name, + url: url, + maxUser: 1, + }; + + if (data.project.trim().length == 0 || data.project.length > 100) { + setError('project name length unexpected'); + return; + } + + if (data.name.trim().length == 0 || data.name.length > 100) { + setError('Repo name length unexpected'); + return; + } + + try { + new URL(data.url); + } catch (e) { + setError('Invalid URL'); + return; + } + + try { + await addRepo(onClose, setError, data); + handleSuccess(data); + handleClose(); + } catch (e) { + if (e.message) { + setError(e.message); + } else { + setError(e.toString()); + } + } + }; + + const inputStyle = { + width: '100%', + }; + + return ( + <> + setTip(false)} + /> + + + {error} + + + Add a repository... + + + + + + + Organization + setProject(e.target.value)} + /> + GitHub Organization + + + + + Name + setName(e.target.value)} + /> + GitHub Repository Name + + + + + URL + setUrl(e.target.value)} + /> + GitHub Repository URL + + + +
+ + +
+
+
+
+
+
+ + ); +} + +AddRepositoryDialog.propTypes = { + onClose: PropTypes.func.isRequired, + open: PropTypes.bool.isRequired, + onSuccess: PropTypes.func.isRequired, +}; + +NewRepo.propTypes = { + onSuccess: PropTypes.func.isRequired, +}; + +export default function NewRepo(props) { + const [open, setOpen] = React.useState(false); + const handleClickOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + return ( +
+ + +
+ ); +} diff --git a/src/ui/views/RepoList/Components/RepoOverview.jsx b/src/ui/views/RepoList/Components/RepoOverview.jsx new file mode 100644 index 00000000..772e8163 --- /dev/null +++ b/src/ui/views/RepoList/Components/RepoOverview.jsx @@ -0,0 +1,782 @@ +import React, { useEffect } from 'react'; +import TableCell from '@material-ui/core/TableCell'; +import Popper from '@material-ui/core/Popper'; +import TableRow from '@material-ui/core/TableRow'; +import Paper from '@material-ui/core/Paper'; +import GridContainer from '../../../components/Grid/GridContainer'; +import GridItem from '../../../components/Grid/GridItem'; +import { + CheckIcon, + ChevronDownIcon, + CodeIcon, + CodeReviewIcon, + CopyIcon, + LawIcon, + PeopleIcon, + TerminalIcon, +} from '@primer/octicons-react'; + +const colors = { + '1C Enterprise': '#814CCC', + '2-Dimensional Array': '#38761D', + '4D': '#004289', + ABAP: '#E8274B', + 'ABAP CDS': '#555e25', + ActionScript: '#882B0F', + Ada: '#02f88c', + 'Adblock Filter List': '#800000', + 'Adobe Font Metrics': '#fa0f00', + Agda: '#315665', + 'AGS Script': '#B9D9FF', + AIDL: '#34EB6B', + AL: '#3AA2B5', + Alloy: '#64C800', + 'Alpine Abuild': '#0D597F', + 'Altium Designer': '#A89663', + AMPL: '#E6EFBB', + AngelScript: '#C7D7DC', + 'Ant Build System': '#A9157E', + Antlers: '#ff269e', + ANTLR: '#9DC3FF', + ApacheConf: '#d12127', + Apex: '#1797c0', + 'API Blueprint': '#2ACCA8', + APL: '#5A8164', + 'Apollo Guidance Computer': '#0B3D91', + AppleScript: '#101F1F', + Arc: '#aa2afe', + AsciiDoc: '#73a0c5', + 'ASP.NET': '#9400ff', + AspectJ: '#a957b0', + Assembly: '#6E4C13', + Astro: '#ff5a03', + Asymptote: '#ff0000', + ATS: '#1ac620', + Augeas: '#9CC134', + AutoHotkey: '#6594b9', + AutoIt: '#1C3552', + 'Avro IDL': '#0040FF', + Awk: '#c30e9b', + Ballerina: '#FF5000', + BASIC: '#ff0000', + Batchfile: '#C1F12E', + Beef: '#a52f4e', + Berry: '#15A13C', + BibTeX: '#778899', + Bicep: '#519aba', + Bikeshed: '#5562ac', + Bison: '#6A463F', + BitBake: '#00bce4', + Blade: '#f7523f', + BlitzBasic: '#00FFAE', + BlitzMax: '#cd6400', + Bluespec: '#12223c', + Boo: '#d4bec1', + Boogie: '#c80fa0', + Brainfuck: '#2F2530', + BrighterScript: '#66AABB', + Brightscript: '#662D91', + Browserslist: '#ffd539', + C: '#555555', + 'C#': '#178600', + 'C++': '#f34b7d', + 'Cabal Config': '#483465', + Cadence: '#00ef8b', + Cairo: '#ff4a48', + CameLIGO: '#3be133', + 'CAP CDS': '#0092d1', + "Cap'n Proto": '#c42727', + Ceylon: '#dfa535', + Chapel: '#8dc63f', + ChucK: '#3f8000', + Circom: '#707575', + Cirru: '#ccccff', + Clarion: '#db901e', + Clarity: '#5546ff', + 'Classic ASP': '#6a40fd', + Clean: '#3F85AF', + Click: '#E4E6F3', + CLIPS: '#00A300', + Clojure: '#db5855', + 'Closure Templates': '#0d948f', + 'Cloud Firestore Security Rules': '#FFA000', + CMake: '#DA3434', + CodeQL: '#140f46', + CoffeeScript: '#244776', + ColdFusion: '#ed2cd6', + 'ColdFusion CFC': '#ed2cd6', + COLLADA: '#F1A42B', + 'Common Lisp': '#3fb68b', + 'Common Workflow Language': '#B5314C', + 'Component Pascal': '#B0CE4E', + Coq: '#d0b68c', + Crystal: '#000100', + CSON: '#244776', + Csound: '#1a1a1a', + 'Csound Document': '#1a1a1a', + 'Csound Score': '#1a1a1a', + CSS: '#563d7c', + CSV: '#237346', + Cuda: '#3A4E3A', + CUE: '#5886E1', + Curry: '#531242', + CWeb: '#00007a', + Cypher: '#34c0eb', + Cython: '#fedf5b', + D: '#ba595e', + Dafny: '#FFEC25', + 'Darcs Patch': '#8eff23', + Dart: '#00B4AB', + DataWeave: '#003a52', + 'Debian Package Control File': '#D70751', + DenizenScript: '#FBEE96', + Dhall: '#dfafff', + 'DirectX 3D File': '#aace60', + DM: '#447265', + Dockerfile: '#384d54', + Dogescript: '#cca760', + Dotenv: '#e5d559', + Dylan: '#6c616e', + E: '#ccce35', + Earthly: '#2af0ff', + Easybuild: '#069406', + eC: '#913960', + 'Ecere Projects': '#913960', + ECL: '#8a1267', + ECLiPSe: '#001d9d', + Ecmarkup: '#eb8131', + EditorConfig: '#fff1f2', + Eiffel: '#4d6977', + EJS: '#a91e50', + Elixir: '#6e4a7e', + Elm: '#60B5CC', + Elvish: '#55BB55', + 'Elvish Transcript': '#55BB55', + 'Emacs Lisp': '#c065db', + EmberScript: '#FFF4F3', + EQ: '#a78649', + Erlang: '#B83998', + Euphoria: '#FF790B', + 'F#': '#b845fc', + 'F*': '#572e30', + Factor: '#636746', + Fancy: '#7b9db4', + Fantom: '#14253c', + Faust: '#c37240', + Fennel: '#fff3d7', + 'FIGlet Font': '#FFDDBB', + 'Filebench WML': '#F6B900', + fish: '#4aae47', + Fluent: '#ffcc33', + FLUX: '#88ccff', + Forth: '#341708', + Fortran: '#4d41b1', + 'Fortran Free Form': '#4d41b1', + FreeBasic: '#141AC9', + FreeMarker: '#0050b2', + Frege: '#00cafe', + Futhark: '#5f021f', + 'G-code': '#D08CF2', + 'Game Maker Language': '#71b417', + GAML: '#FFC766', + GAMS: '#f49a22', + GAP: '#0000cc', + 'GCC Machine Description': '#FFCFAB', + GDScript: '#355570', + GEDCOM: '#003058', + 'Gemfile.lock': '#701516', + Gemini: '#ff6900', + Genero: '#63408e', + 'Genero Forms': '#d8df39', + Genie: '#fb855d', + Genshi: '#951531', + 'Gentoo Ebuild': '#9400ff', + 'Gentoo Eclass': '#9400ff', + 'Gerber Image': '#d20b00', + Gherkin: '#5B2063', + 'Git Attributes': '#F44D27', + 'Git Config': '#F44D27', + 'Git Revision List': '#F44D27', + Gleam: '#ffaff3', + GLSL: '#5686a5', + Glyph: '#c1ac7f', + Gnuplot: '#f0a9f0', + Go: '#00ADD8', + 'Go Checksums': '#00ADD8', + 'Go Module': '#00ADD8', + 'Godot Resource': '#355570', + Golo: '#88562A', + Gosu: '#82937f', + Grace: '#615f8b', + Gradle: '#02303a', + 'Grammatical Framework': '#ff0000', + GraphQL: '#e10098', + 'Graphviz (DOT)': '#2596be', + Groovy: '#4298b8', + 'Groovy Server Pages': '#4298b8', + GSC: '#FF6800', + Hack: '#878787', + Haml: '#ece2a9', + Handlebars: '#f7931e', + HAProxy: '#106da9', + Harbour: '#0e60e3', + Haskell: '#5e5086', + Haxe: '#df7900', + HCL: '#844FBA', + HiveQL: '#dce200', + HLSL: '#aace60', + HOCON: '#9ff8ee', + HolyC: '#ffefaf', + hoon: '#00b171', + HTML: '#e34c26', + 'HTML+ECR': '#2e1052', + 'HTML+EEX': '#6e4a7e', + 'HTML+ERB': '#701516', + 'HTML+PHP': '#4f5d95', + 'HTML+Razor': '#512be4', + HTTP: '#005C9C', + HXML: '#f68712', + Hy: '#7790B2', + IDL: '#a3522f', + Idris: '#b30000', + 'Ignore List': '#000000', + 'IGOR Pro': '#0000cc', + 'ImageJ Macro': '#99AAFF', + Imba: '#16cec6', + INI: '#d1dbe0', + 'Inno Setup': '#264b99', + Io: '#a9188d', + Ioke: '#078193', + Isabelle: '#FEFE00', + 'Isabelle ROOT': '#FEFE00', + J: '#9EEDFF', + Janet: '#0886a5', + 'JAR Manifest': '#b07219', + Jasmin: '#d03600', + Java: '#b07219', + 'Java Properties': '#2A6277', + 'Java Server Pages': '#2A6277', + JavaScript: '#f1e05a', + 'JavaScript+ERB': '#f1e05a', + JCL: '#d90e09', + 'Jest Snapshot': '#15c213', + 'JetBrains MPS': '#21D789', + JFlex: '#DBCA00', + Jinja: '#a52a22', + Jison: '#56b3cb', + 'Jison Lex': '#56b3cb', + Jolie: '#843179', + jq: '#c7254e', + JSON: '#292929', + 'JSON with Comments': '#292929', + JSON5: '#267CB9', + JSONiq: '#40d47e', + JSONLD: '#0c479c', + Jsonnet: '#0064bd', + Julia: '#a270ba', + 'Jupyter Notebook': '#DA5B0B', + Just: '#384d54', + 'Kaitai Struct': '#773b37', + KakouneScript: '#6f8042', + KerboScript: '#41adf0', + 'KiCad Layout': '#2f4aab', + 'KiCad Legacy Layout': '#2f4aab', + 'KiCad Schematic': '#2f4aab', + Kotlin: '#A97BFF', + KRL: '#28430A', + kvlang: '#1da6e0', + LabVIEW: '#fede06', + Lark: '#2980B9', + Lasso: '#999999', + Latte: '#f2a542', + Less: '#1d365d', + Lex: '#DBCA00', + LFE: '#4C3023', + LigoLANG: '#0e74ff', + LilyPond: '#9ccc7c', + Liquid: '#67b8de', + 'Literate Agda': '#315665', + 'Literate CoffeeScript': '#244776', + 'Literate Haskell': '#5e5086', + LiveScript: '#499886', + LLVM: '#185619', + Logtalk: '#295b9a', + LOLCODE: '#cc9900', + LookML: '#652B81', + LSL: '#3d9970', + Lua: '#000080', + Macaulay2: '#d8ffff', + Makefile: '#427819', + Mako: '#7e858d', + Markdown: '#083fa1', + Marko: '#42bff2', + Mask: '#f97732', + Mathematica: '#dd1100', + MATLAB: '#e16737', + Max: '#c4a79c', + MAXScript: '#00a6a6', + mcfunction: '#E22837', + Mercury: '#ff2b2b', + Mermaid: '#ff3670', + Meson: '#007800', + Metal: '#8f14e9', + MiniYAML: '#ff1111', + Mint: '#02b046', + Mirah: '#c7a938', + 'mIRC Script': '#3d57c3', + MLIR: '#5EC8DB', + Modelica: '#de1d31', + 'Modula-2': '#10253f', + 'Modula-3': '#223388', + 'Monkey C': '#8D6747', + MoonScript: '#ff4585', + Motoko: '#fbb03b', + 'Motorola 68K Assembly': '#005daa', + Move: '#4a137a', + MQL4: '#62A8D6', + MQL5: '#4A76B8', + MTML: '#b7e1f4', + mupad: '#244963', + Mustache: '#724b3b', + nanorc: '#2d004d', + Nasal: '#1d2c4e', + NCL: '#28431f', + Nearley: '#990000', + Nemerle: '#3d3c6e', + nesC: '#94B0C7', + NetLinx: '#0aa0ff', + 'NetLinx+ERB': '#747faa', + NetLogo: '#ff6375', + NewLisp: '#87AED7', + Nextflow: '#3ac486', + Nginx: '#009639', + Nim: '#ffc200', + Nit: '#009917', + Nix: '#7e7eff', + 'NPM Config': '#cb3837', + Nu: '#c9df40', + NumPy: '#9C8AF9', + Nunjucks: '#3d8137', + NWScript: '#111522', + 'OASv2-json': '#85ea2d', + 'OASv2-yaml': '#85ea2d', + 'OASv3-json': '#85ea2d', + 'OASv3-yaml': '#85ea2d', + 'Objective-C': '#438eff', + 'Objective-C++': '#6866fb', + 'Objective-J': '#ff0c5a', + ObjectScript: '#424893', + OCaml: '#3be133', + Odin: '#60AFFE', + Omgrofl: '#cabbff', + ooc: '#b0b77e', + Opal: '#f7ede0', + 'Open Policy Agent': '#7d9199', + 'OpenAPI Specification v2': '#85ea2d', + 'OpenAPI Specification v3': '#85ea2d', + OpenCL: '#ed2e2d', + 'OpenEdge ABL': '#5ce600', + OpenQASM: '#AA70FF', + OpenSCAD: '#e5cd45', + 'Option List': '#476732', + Org: '#77aa99', + Oxygene: '#cdd0e3', + Oz: '#fab738', + P4: '#7055b5', + Pan: '#cc0000', + Papyrus: '#6600cc', + Parrot: '#f3ca0a', + Pascal: '#E3F171', + Pawn: '#dbb284', + PDDL: '#0d00ff', + 'PEG.js': '#234d6b', + Pep8: '#C76F5B', + Perl: '#0298c3', + PHP: '#4F5D95', + PicoLisp: '#6067af', + PigLatin: '#fcd7de', + Pike: '#005390', + PlantUML: '#fbbd16', + PLpgSQL: '#336790', + PLSQL: '#dad8d8', + PogoScript: '#d80074', + Polar: '#ae81ff', + Portugol: '#f8bd00', + PostCSS: '#dc3a0c', + PostScript: '#da291c', + 'POV-Ray SDL': '#6bac65', + PowerBuilder: '#8f0f8d', + PowerShell: '#012456', + Prisma: '#0c344b', + Processing: '#0096D8', + Procfile: '#3B2F63', + Prolog: '#74283c', + Promela: '#de0000', + 'Propeller Spin': '#7fa2a7', + Pug: '#a86454', + Puppet: '#302B6D', + PureBasic: '#5a6986', + PureScript: '#1D222D', + Pyret: '#ee1e10', + Python: '#3572A5', + 'Python console': '#3572A5', + 'Python traceback': '#3572A5', + q: '#0040cd', + 'Q#': '#fed659', + QML: '#44a51c', + 'Qt Script': '#00b841', + Quake: '#882233', + R: '#198CE7', + Racket: '#3c5caa', + Ragel: '#9d5200', + Raku: '#0000fb', + RAML: '#77d9fb', + Rascal: '#fffaa0', + RDoc: '#701516', + Reason: '#ff5847', + ReasonLIGO: '#ff5847', + Rebol: '#358a5b', + 'Record Jar': '#0673ba', + Red: '#f50000', + 'Regular Expression': '#009a00', + "Ren'Py": '#ff7f7f', + ReScript: '#ed5051', + reStructuredText: '#141414', + REXX: '#d90e09', + Ring: '#2D54CB', + Riot: '#A71E49', + RMarkdown: '#198ce7', + RobotFramework: '#00c0b5', + Roff: '#ecdebe', + 'Roff Manpage': '#ecdebe', + Rouge: '#cc0088', + 'RouterOS Script': '#DE3941', + RPGLE: '#2BDE21', + Ruby: '#701516', + RUNOFF: '#665a4e', + Rust: '#dea584', + SaltStack: '#646464', + SAS: '#B34936', + Sass: '#a53b70', + Scala: '#c22d40', + Scaml: '#bd181a', + Scenic: '#fdc700', + Scheme: '#1e4aec', + Scilab: '#ca0f21', + SCSS: '#c6538c', + sed: '#64b970', + Self: '#0579aa', + ShaderLab: '#222c37', + Shell: '#89e051', + 'ShellCheck Config': '#cecfcb', + Shen: '#120F14', + 'Simple File Verification': '#C9BFED', + Singularity: '#64E6AD', + Slash: '#007eff', + Slice: '#003fa2', + Slim: '#2b2b2b', + Smalltalk: '#596706', + Smarty: '#f0c040', + Smithy: '#c44536', + SmPL: '#c94949', + Snakemake: '#419179', + Solidity: '#AA6746', + SourcePawn: '#f69e1d', + SPARQL: '#0C4597', + SQF: '#3F3F3F', + SQL: '#e38c00', + SQLPL: '#e38c00', + Squirrel: '#800000', + 'SRecode Template': '#348a34', + Stan: '#b2011d', + 'Standard ML': '#dc566d', + Starlark: '#76d275', + Stata: '#1a5f91', + STL: '#373b5e', + StringTemplate: '#3fb34f', + Stylus: '#ff6347', + 'SubRip Text': '#9e0101', + SugarSS: '#2fcc9f', + SuperCollider: '#46390b', + Svelte: '#ff3e00', + SVG: '#ff9900', + Sway: '#dea584', + Swift: '#F05138', + SystemVerilog: '#DAE1C2', + Talon: '#333333', + Tcl: '#e4cc98', + Terra: '#00004c', + TeX: '#3D6117', + Textile: '#ffe7ac', + 'TextMate Properties': '#df66e4', + Thrift: '#D12127', + 'TI Program': '#A0AA87', + TLA: '#4b0079', + TOML: '#9c4221', + TSQL: '#e38c00', + TSV: '#237346', + TSX: '#3178c6', + Turing: '#cf142b', + Twig: '#c1d026', + TXL: '#0178b8', + TypeScript: '#3178c6', + 'Unified Parallel C': '#4e3617', + 'Unity3D Asset': '#222c37', + Uno: '#9933cc', + UnrealScript: '#a54c4d', + UrWeb: '#ccccee', + V: '#4f87c4', + Vala: '#a56de2', + 'Valve Data Format': '#f26025', + VBA: '#867db1', + VBScript: '#15dcdc', + VCL: '#148AA8', + 'Velocity Template Language': '#507cff', + Verilog: '#b2b7f8', + VHDL: '#adb2cb', + 'Vim Help File': '#199f4b', + 'Vim Script': '#199f4b', + 'Vim Snippet': '#199f4b', + 'Visual Basic .NET': '#945db7', + 'Visual Basic 6.0': '#2c6353', + Volt: '#1F1F1F', + Vue: '#41b883', + Vyper: '#2980b9', + wdl: '#42f1f4', + 'Web Ontology Language': '#5b70bd', + WebAssembly: '#04133b', + Whiley: '#d5c397', + Wikitext: '#fc5757', + 'Windows Registry Entries': '#52d5ff', + wisp: '#7582D1', + 'Witcher Script': '#ff0000', + Wollok: '#a23738', + 'World of Warcraft Addon Data': '#f7e43f', + Wren: '#383838', + X10: '#4B6BEF', + xBase: '#403a40', + XC: '#99DA07', + XML: '#0060ac', + 'XML Property List': '#0060ac', + Xojo: '#81bd41', + Xonsh: '#285EEF', + XQuery: '#5232e7', + XSLT: '#EB8CEB', + Xtend: '#24255d', + Yacc: '#4B6C4B', + YAML: '#cb171e', + YARA: '#220000', + YASnippet: '#32AB90', + Yul: '#794932', + ZAP: '#0d665e', + ZenScript: '#00BCD1', + Zephir: '#118f9e', + Zig: '#ec915c', + ZIL: '#dc75e5', + Zimpl: '#d67711', +}; + +import axios from 'axios'; +import moment from 'moment'; + +export default function Repositories(props) { + const [anchorEl, setAnchorEl] = React.useState(null); + const [open, setOpen] = React.useState(false); + const [placement, setPlacement] = React.useState(); + const [cloneURL, setCloneURL] = React.useState(null); + const [isCopied, setIsCopied] = React.useState(false); + const [github, setGitHub] = React.useState({}); + + useEffect(() => { + getGitHubRepository(); + }, [props.data.project, props.data.name]); + + const getGitHubRepository = async () => { + await axios + .get(`https://api.github.com/repos/${props.data.project}/${props.data.name}`) + .then((res) => { + setGitHub(res.data); + }); + }; + + const handleClick = (newPlacement, org, name) => (event) => { + setIsCopied(false); + setAnchorEl(event.currentTarget); + setOpen((prev) => placement !== newPlacement || !prev); + setPlacement(newPlacement); + setCloneURL(`${import.meta.env.VITE_SERVER_URI}/${org}/${name}.git`); + }; + + return ( + + +
+ + + {props.data.project}/{props.data.name} + + + {github.parent && ( + + Forked from{' '} + + {github.parent.full_name} + + + )} + {github.description &&

{github.description}

} + + {github.language && ( + + + {github.language} + + )} + {github.license && ( + + {' '} + {github.license.spdx_id} + + )} + + {' '} + {props.data.users?.canPush?.length || 0} + + + {' '} + + {props.data.users?.canAuthorise?.length || 0} + + + {(github.created_at || github.updated_at || github.pushed_at) && ( + + Last updated{' '} + {moment + .max([ + moment(github.created_at), + moment(github.updated_at), + moment(github.pushed_at), + ]) + .fromNow()} + + )} + +
+
+ +
+ {' '} + + {' '} + Code + + + + +
+ {' '} + + Clone + +
+
+ + {cloneURL} + + + {!isCopied && ( + { + navigator.clipboard.writeText(`git clone ${cloneURL}`); + setIsCopied(true); + }} + > + + + )} + {isCopied && ( + + + + )} + +
+
+
+ + Use Git and run this command in your IDE or Terminal 👍 + +
+
+
+
+
+
+
+ ); +} diff --git a/src/ui/views/RepoList/Components/Repositories.jsx b/src/ui/views/RepoList/Components/Repositories.jsx index 777a61c7..4970858e 100644 --- a/src/ui/views/RepoList/Components/Repositories.jsx +++ b/src/ui/views/RepoList/Components/Repositories.jsx @@ -1,34 +1,31 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useContext } from 'react'; import { makeStyles } from '@material-ui/core/styles'; import { useNavigate } from 'react-router-dom'; -import Button from '@material-ui/core/Button'; import Table from '@material-ui/core/Table'; import TableBody from '@material-ui/core/TableBody'; -import TableCell from '@material-ui/core/TableCell'; import TableContainer from '@material-ui/core/TableContainer'; -import TableHead from '@material-ui/core/TableHead'; -import TableRow from '@material-ui/core/TableRow'; -import Paper from '@material-ui/core/Paper'; -import { Navigate } from 'react-router-dom'; -import styles from '../../../assets/jss/material-dashboard-react/views/dashboardStyle.js'; +import styles from '../../../assets/jss/material-dashboard-react/views/dashboardStyle'; import { getRepos } from '../../../services/repo'; +import GridContainer from '../../../components/Grid/GridContainer'; +import GridItem from '../../../components/Grid/GridItem'; +import NewRepo from './NewRepo'; +import RepoOverview from './RepoOverview'; +import { UserContext } from '../../../../context'; +import PropTypes from 'prop-types'; export default function Repositories(props) { const useStyles = makeStyles(styles); const classes = useStyles(); const [data, setData] = useState([]); - const [auth, setAuth] = useState(true); + const [, setAuth] = useState(true); const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); - const history = useNavigate(); - - const openRepo = (repo) => history.push(`/admin/repo/${repo}`); + const navigate = useNavigate(); + const openRepo = (repo) => navigate(`/admin/repo/${repo}`, { replace: true }); + const { user } = useContext(UserContext); useEffect(() => { const query = {}; - for (const k in props) { if (!k) continue; query[k] = props[k]; @@ -36,40 +33,59 @@ export default function Repositories(props) { getRepos(setIsLoading, setData, setAuth, setIsError, query); }, [props]); - if (isLoading) return
Loading ...
; + const refresh = async (repo) => { + console.log('refresh:', repo); + setData([...data, repo]); + }; + + if (isLoading) return
Loading...
; if (isError) return
Something went wrong ...
; - if (!auth) return ; + const addrepoButton = user.admin ? ( + + + + ) : ( + + ); + + return ( + + ); +} + +GetGridContainerLayOut.propTypes = { + classes: PropTypes.object, + openRepo: PropTypes.func.isRequired, + data: PropTypes.array, + repoButton: PropTypes.object, +}; + +function GetGridContainerLayOut(props) { return ( - - - - - Actions - Project - Name - Url - - - - {data.map((row) => ( - - - - - {row.project} - {row.name} - {row.url} - - ))} - -
-
+ + {props.repoButton} + + + + + {props.data.map((row) => { + if (row.project && row.name) { + return ; + } + })} + +
+
+
+
); } diff --git a/src/ui/views/RepoList/Components/TabList.jsx b/src/ui/views/RepoList/Components/TabList.jsx index 849c7267..40d86561 100644 --- a/src/ui/views/RepoList/Components/TabList.jsx +++ b/src/ui/views/RepoList/Components/TabList.jsx @@ -1,9 +1,6 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; -import GridItem from '../../../components/Grid/GridItem.jsx'; -import GridContainer from '../../../components/Grid/GridContainer.jsx'; -import CustomTabs from '../../../components/CustomTabs/CustomTabs'; +import GridItem from '../../../components/Grid/GridItem'; +import GridContainer from '../../../components/Grid/GridContainer'; import Repositories from './Repositories'; export default function Dashboard() { @@ -11,16 +8,7 @@ export default function Dashboard() {
- , - }, - ]} - /> +
diff --git a/src/ui/views/RepoList/RepoList.jsx b/src/ui/views/RepoList/RepoList.jsx index 2dc32d94..7b0f2272 100644 --- a/src/ui/views/RepoList/RepoList.jsx +++ b/src/ui/views/RepoList/RepoList.jsx @@ -1,30 +1,13 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; -import Icon from '@material-ui/core/Icon'; -import GridItem from '../../components/Grid/GridItem.jsx'; -import GridContainer from '../../components/Grid/GridContainer.jsx'; -import Card from '../../components/Card/Card.jsx'; -import CardIcon from '../../components/Card/CardIcon.jsx'; -import CardBody from '../../components/Card/CardBody.jsx'; -import CardHeader from '../../components/Card/CardHeader.jsx'; +import GridItem from '../../components/Grid/GridItem'; +import GridContainer from '../../components/Grid/GridContainer'; import TabList from './Components/TabList'; export default function RepoList(props) { return ( - - - - content_copy -

Admin

-
-
- - - -
+
); diff --git a/src/ui/views/User/User.jsx b/src/ui/views/User/User.jsx index bd287c05..c8b46ebe 100644 --- a/src/ui/views/User/User.jsx +++ b/src/ui/views/User/User.jsx @@ -1,24 +1,19 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React, { useState, useEffect } from 'react'; -import { Navigate, useParams } from 'react-router-dom'; -import Icon from '@material-ui/core/Icon'; -import GridItem from '../../components/Grid/GridItem.jsx'; -import GridContainer from '../../components/Grid/GridContainer.jsx'; -import Card from '../../components/Card/Card.jsx'; -import CardIcon from '../../components/Card/CardIcon.jsx'; -import CardBody from '../../components/Card/CardBody.jsx'; -import CardHeader from '../../components/Card/CardHeader.jsx'; -// import Button from '../../components/CustomButtons/Button.js'; -// import PropTypes from 'prop-types'; -import TextField from '@material-ui/core/TextField'; -import Checkbox from '@material-ui/core/Checkbox'; -// import FormControlLabel from '@material-ui/core/FormControlLabel'; -// import FormControl from '@material-ui/core/FormControl'; +import { Navigate, useNavigate, useParams } from 'react-router-dom'; +import GridItem from '../../components/Grid/GridItem'; +import GridContainer from '../../components/Grid/GridContainer'; +import Card from '../../components/Card/Card'; +import CardBody from '../../components/Card/CardBody'; +import Button from '../../components/CustomButtons/Button'; import FormLabel from '@material-ui/core/FormLabel'; -import { getUser } from '../../services/user.js'; +import { getUser, updateUser, getUserLoggedIn } from '../../services/user'; import { makeStyles } from '@material-ui/core/styles'; +import { LogoGithubIcon } from '@primer/octicons-react'; +import CloseRounded from '@material-ui/icons/CloseRounded'; +import { Check, Save } from '@material-ui/icons'; +import { TextField } from '@material-ui/core'; + const useStyles = makeStyles((theme) => ({ root: { '& .MuiTextField-root': { @@ -34,74 +29,140 @@ export default function Dashboard() { const [auth, setAuth] = useState(true); const [isLoading, setIsLoading] = useState(true); const [isError, setIsError] = useState(false); + const [isProfile, setIsProfile] = useState(false); + const [isAdmin, setIsAdmin] = useState(false); + const [gitAccount, setGitAccount] = useState(''); + const navigate = useNavigate(); const { id } = useParams(); useEffect(() => { - // eslint-disable-next-line react/prop-types + if (id == null) { + setIsProfile(true); + } + if (id) { getUser(setIsLoading, setData, setAuth, setIsError, id); + getUserLoggedIn(setIsLoading, setIsAdmin, setIsError, setAuth); } else { console.log('getting user data'); + setIsProfile(true); getUser(setIsLoading, setData, setAuth, setIsError); } - }, [id]); + }, []); - if (isLoading) return
Loading ...
; + if (isLoading) return
Loading...
; if (isError) return
Something went wrong ...
; - if (!auth) return ; + if (!auth && window.location.pathname === '/admin/profile') { + return ; + } + + const updateProfile = async () => { + try { + data.gitAccount = escapeHTML(gitAccount); + await updateUser(data); + navigate(`/admin/user/${data.username}`); + } catch { + setIsError(true); + } + }; + + const UpdateButton = () => ( + + ); - console.log(data); + const escapeHTML = (str) => { + return str + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') + .replace(/\\/g, ''') + .replace(/\//g, '/'); + }; return ( -
+ - - - content_copy -

User Details

-
-
-
-
- - - - + + + {data.gitAccount && ( + + + + )} + + Name + {data.displayName} - - + + Role + {data.title} - - Admin - + + E-mail + {data.email} - - + {data.gitAccount && ( + + GitHub Username + + {data.gitAccount} + + + )} + + Administrator + {data.admin ? ( + + + + ) : ( + + )} + {isProfile || isAdmin ? ( +
+
+
+ + What is your username? + +
+ setGitAccount(e.target.value)} + /> + +
+
+
+ ) : null}
diff --git a/src/ui/views/UserList/Components/NewUser.jsx b/src/ui/views/UserList/Components/NewUser.jsx deleted file mode 100644 index 26d15192..00000000 --- a/src/ui/views/UserList/Components/NewUser.jsx +++ /dev/null @@ -1,160 +0,0 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ -import React, { useState } from 'react'; -import PropTypes from 'prop-types'; -import InputLabel from '@material-ui/core/InputLabel'; -import Input from '@material-ui/core/Input'; -import FormControl from '@material-ui/core/FormControl'; -import Checkbox from '@material-ui/core/Checkbox'; -import FormHelperText from '@material-ui/core/FormHelperText'; -import GridItem from '../../../components/Grid/GridItem.jsx'; -import GridContainer from '../../../components/Grid/GridContainer.jsx'; -import Card from '../../../components/Card/Card.jsx'; -import CardBody from '../../../components/Card/CardBody.jsx'; -import Button from '../../../components/CustomButtons/Button.jsx'; -import DialogTitle from '@material-ui/core/DialogTitle'; -import Dialog from '@material-ui/core/Dialog'; - -import { createUser } from '../../../services/user.js'; - -function CreateUserDialog(props) { - const [username, setUsername] = useState(''); - const [email, setEmail] = useState(''); - const [gitAccount, setGitAccount] = useState(''); - const [admin, setAdmin] = useState(false); - const [error, setError] = useState(''); - const { onClose, open } = props; - - const handleClose = () => { - onClose(); - }; - - const create = async () => { - const data = { - username: username, - gitAccount: gitAccount, - email: email, - admin: admin, - }; - - try { - await createUser(data); - handleClose(); - } catch (e) { - if (e.message) { - setError(e.response.data.message); - } else { - setError(e.toString()); - } - } - }; - - return ( - - - {error} - - - - - - - Username - setUsername(e.target.value)} - /> - - The username - - - - - - Email address - setEmail(e.target.value)} - /> - - The users email - an email will be sent to the user with at - temporary password - - - - - - GitAccount Name - setGitAccount(e.target.value)} - /> - - The users Git Accout user name - - - - - - Is an Admin - setAdmin(e.target.value)} - /> - - Admin users are able to add repositories and create users - - - - - - - - - - - - ); -} - -CreateUserDialog.propTypes = { - onClose: PropTypes.func.isRequired, - open: PropTypes.bool.isRequired, -}; - -export default function NewUser(props) { - const [open, setOpen] = React.useState(false); - - const handleClickOpen = () => { - setOpen(true); - }; - - const handleClose = () => { - setOpen(false); - }; - - return ( -
- - -
- ); -} diff --git a/src/ui/views/UserList/Components/TabList.jsx b/src/ui/views/UserList/Components/TabList.jsx index 0fd50abd..2d8d69e8 100644 --- a/src/ui/views/UserList/Components/TabList.jsx +++ b/src/ui/views/UserList/Components/TabList.jsx @@ -1,9 +1,6 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React from 'react'; -import GridItem from '../../../components/Grid/GridItem.jsx'; -import GridContainer from '../../../components/Grid/GridContainer.jsx'; -import CustomTabs from '../../../components/CustomTabs/CustomTabs'; +import GridItem from '../../../components/Grid/GridItem'; +import GridContainer from '../../../components/Grid/GridContainer'; import UserList from './UserList'; export default function Dashboard() { @@ -11,16 +8,7 @@ export default function Dashboard() {
- , - }, - ]} - /> +
diff --git a/src/ui/views/UserList/Components/UserList.jsx b/src/ui/views/UserList/Components/UserList.jsx index 4ac04c99..b1273cb5 100644 --- a/src/ui/views/UserList/Components/UserList.jsx +++ b/src/ui/views/UserList/Components/UserList.jsx @@ -1,9 +1,7 @@ -/* eslint-disable max-len */ -/* eslint-disable require-jsdoc */ import React, { useState, useEffect } from 'react'; import { makeStyles } from '@material-ui/core/styles'; -import GridItem from '../../../components/Grid/GridItem.jsx'; -import GridContainer from '../../../components/Grid/GridContainer.jsx'; +import GridItem from '../../../components/Grid/GridItem'; +import GridContainer from '../../../components/Grid/GridContainer'; import { useNavigate } from 'react-router-dom'; import Button from '@material-ui/core/Button'; import Table from '@material-ui/core/Table'; @@ -13,21 +11,21 @@ import TableContainer from '@material-ui/core/TableContainer'; import TableHead from '@material-ui/core/TableHead'; import TableRow from '@material-ui/core/TableRow'; import Paper from '@material-ui/core/Paper'; -import { Navigate } from 'react-router-dom'; -import styles from '../../../assets/jss/material-dashboard-react/views/dashboardStyle.js'; +import styles from '../../../assets/jss/material-dashboard-react/views/dashboardStyle'; import { getUsers } from '../../../services/user'; -import NewUser from './NewUser'; + +import { CloseRounded, Check, KeyboardArrowRight } from '@material-ui/icons'; export default function UserList(props) { const useStyles = makeStyles(styles); const classes = useStyles(); const [data, setData] = useState([]); - const [auth, setAuth] = useState(true); + const [, setAuth] = useState(true); const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); - const history = useNavigate(); + const navigate = useNavigate(); - const openUser = (username) => history.push(`/admin/user/${username}`); + const openUser = (username) => navigate(`/admin/user/${username}`, { replace: true }); useEffect(() => { const query = {}; @@ -39,43 +37,59 @@ export default function UserList(props) { getUsers(setIsLoading, setData, setAuth, setIsError, query); }, [props]); - if (isLoading) return
Loading ...
; - if (isError) return
Something went wrong ...
; - if (!auth) return ; + if (isLoading) return
Loading...
; + if (isError) return
Something went wrong...
; return ( - - - - +
- Actions - Username - email - Git Account - Admin + Name + Role + E-mail + GitHub Username + Administrator + {data.map((row) => ( - + {row.displayName} + {row.title} + + {row.email} + + + + {row.gitAccount} + + + + {row.admin ? ( + + + + ) : ( + + )} + + - {row.username} - {row.email} - {row.gitAccount} - {row.admin.toString()} ))} diff --git a/src/ui/views/UserList/UserList.jsx b/src/ui/views/UserList/UserList.jsx index 06e56b27..7e94ecf1 100644 --- a/src/ui/views/UserList/UserList.jsx +++ b/src/ui/views/UserList/UserList.jsx @@ -1,33 +1,13 @@ import React from 'react'; -import Icon from '@material-ui/core/Icon'; -import GridItem from '../../components/Grid/GridItem.jsx'; -import GridContainer from '../../components/Grid/GridContainer.jsx'; -import Card from '../../components/Card/Card.jsx'; -import CardIcon from '../../components/Card/CardIcon.jsx'; -import CardBody from '../../components/Card/CardBody.jsx'; -import CardHeader from '../../components/Card/CardHeader.jsx'; +import GridItem from '../../components/Grid/GridItem'; +import GridContainer from '../../components/Grid/GridContainer'; import TabList from './Components/TabList'; -/** - * Renders a list of users. - * - * @return {JSX.Element} The rendered component. - */ export default function UserList() { return ( - - - - content_copy -

Admin

-
-
- - - -
+
); diff --git a/test/1.test.js b/test/1.test.js index 94e4e80a..ad67cff6 100644 --- a/test/1.test.js +++ b/test/1.test.js @@ -8,12 +8,13 @@ chai.should(); // Use this test as a template describe('init', async () => { + let app; before(async function () { app = await service.start(); }); it('should not be logged in', async function () { - const res = await chai.request(app).get('/auth/profile'); + const res = await chai.request(app).get('/api/auth/profile'); res.should.have.status(401); }); diff --git a/test/addRepoTest.test.js b/test/addRepoTest.test.js index d667e7a8..04983f63 100644 --- a/test/addRepoTest.test.js +++ b/test/addRepoTest.test.js @@ -1,4 +1,3 @@ -/* eslint-disable max-len */ // Import the dependencies for testing const chai = require('chai'); const chaiHttp = require('chai-http'); @@ -28,12 +27,12 @@ describe('add new repo', async () => { await db.deleteRepo('test-repo'); await db.deleteUser('u1'); await db.deleteUser('u2'); - await db.createUser('u1', 'abc', '', 'test', true, true, true, false); - await db.createUser('u2', 'abc', '', 'test', true, true, true, false); + await db.createUser('u1', 'abc', 'test@test.com', 'test', true); + await db.createUser('u2', 'abc', 'test@test.com', 'test', true); }); it('login', async function () { - const res = await chai.request(app).post('/auth/login').send({ + const res = await chai.request(app).post('/api/auth/login').send({ username: 'admin', password: 'admin', }); @@ -42,15 +41,11 @@ describe('add new repo', async () => { }); it('create a new repo', async function () { - const res = await chai - .request(app) - .post('/api/v1/repo') - .set('Cookie', `${cookie}`) - .send({ - project: 'finos', - name: 'test-repo', - url: 'https://github.com/finos/test-repo.git', - }); + const res = await chai.request(app).post('/api/v1/repo').set('Cookie', `${cookie}`).send({ + project: 'finos', + name: 'test-repo', + url: 'https://github.com/finos/test-repo.git', + }); res.should.have.status(200); const repo = await db.getRepo('test-repo'); @@ -173,6 +168,23 @@ describe('add new repo', async () => { repo.users.canAuthorise.length.should.equal(1); }); + it('Valid user push permission on repo', async function () { + const res = await chai + .request(app) + .patch('/api/v1/repo/test-repo/user/authorise') + .set('Cookie', `${cookie}`) + .send({ username: 'u2' }); + + res.should.have.status(200); + const isAllowed = await db.isUserPushAllowed('test-repo', 'u2'); + expect(isAllowed).to.be.true; + }); + + it('Invalid user push permission on repo', async function () { + const isAllowed = await db.isUserPushAllowed('test-repo', 'test'); + expect(isAllowed).to.be.false; + }); + after(async function () { await service.httpServer.close(); }); diff --git a/test/testCheckRepoInAuthList.test.js b/test/testCheckRepoInAuthList.test.js index 9b1eed03..19d161c1 100644 --- a/test/testCheckRepoInAuthList.test.js +++ b/test/testCheckRepoInAuthList.test.js @@ -1,5 +1,3 @@ -/* eslint-disable max-len */ - const chai = require('chai'); const actions = require('../src/proxy/actions/Action'); const processor = require('../src/proxy/processors/push-action/checkRepoInAuthorisedList'); @@ -16,25 +14,13 @@ const authList = () => { describe('Check a Repo is in the authorised list', async () => { it('Should set ok=true if repo in whitelist', async () => { - const action = new actions.Action( - '123', - 'type', - 'get', - 1234, - 'thisproject/repo-is-ok', - ); + const action = new actions.Action('123', 'type', 'get', 1234, 'thisproject/repo-is-ok'); const result = await processor.exec(null, action, authList); expect(result.error).to.be.false; }); it('Should set ok=false if not in authorised', async () => { - const action = new actions.Action( - '123', - 'type', - 'get', - 1234, - 'thisproject/repo-is-not-ok', - ); + const action = new actions.Action('123', 'type', 'get', 1234, 'thisproject/repo-is-not-ok'); const result = await processor.exec(null, action, authList); expect(result.error).to.be.true; }); diff --git a/test/testConfig.js b/test/testConfig.test.js similarity index 78% rename from test/testConfig.js rename to test/testConfig.test.js index 072f8a66..df5f7461 100644 --- a/test/testConfig.js +++ b/test/testConfig.test.js @@ -1,4 +1,3 @@ -/* eslint-disable max-len */ const chai = require('chai'); const fs = require('fs'); const path = require('path'); @@ -13,16 +12,10 @@ describe('default configuration', function () { const config = require('../src/config'); config.logConfiguration(); - expect(config.getAuthentication()).to.be.eql( - defaultSettings.authentication[0], - ); + expect(config.getAuthentication()).to.be.eql(defaultSettings.authentication[0]); expect(config.getDatabase()).to.be.eql(defaultSettings.sink[0]); - expect(config.getTempPasswordConfig()).to.be.eql( - defaultSettings.tempPassword, - ); - expect(config.getAuthorisedList()).to.be.eql( - defaultSettings.authorisedList, - ); + expect(config.getTempPasswordConfig()).to.be.eql(defaultSettings.tempPassword); + expect(config.getAuthorisedList()).to.be.eql(defaultSettings.authorisedList); }); after(function () { delete require.cache[require.resolve('../src/config')]; @@ -54,13 +47,9 @@ describe('user configuration', function () { const config = require('../src/config'); expect(config.getAuthorisedList()).to.be.eql(user.authorisedList); - expect(config.getAuthentication()).to.be.eql( - defaultSettings.authentication[0], - ); + expect(config.getAuthentication()).to.be.eql(defaultSettings.authentication[0]); expect(config.getDatabase()).to.be.eql(defaultSettings.sink[0]); - expect(config.getTempPasswordConfig()).to.be.eql( - defaultSettings.tempPassword, - ); + expect(config.getTempPasswordConfig()).to.be.eql(defaultSettings.tempPassword); }); it('should override default settings for authentication', function () { @@ -77,13 +66,9 @@ describe('user configuration', function () { const config = require('../src/config'); expect(config.getAuthentication()).to.be.eql(user.authentication[0]); - expect(config.getAuthentication()).to.not.be.eql( - defaultSettings.authentication[0], - ); + expect(config.getAuthentication()).to.not.be.eql(defaultSettings.authentication[0]); expect(config.getDatabase()).to.be.eql(defaultSettings.sink[0]); - expect(config.getTempPasswordConfig()).to.be.eql( - defaultSettings.tempPassword, - ); + expect(config.getTempPasswordConfig()).to.be.eql(defaultSettings.tempPassword); }); it('should override default settings for database', function () { @@ -101,12 +86,8 @@ describe('user configuration', function () { expect(config.getDatabase()).to.be.eql(user.sink[0]); expect(config.getDatabase()).to.not.be.eql(defaultSettings.sink[0]); - expect(config.getAuthentication()).to.be.eql( - defaultSettings.authentication[0], - ); - expect(config.getTempPasswordConfig()).to.be.eql( - defaultSettings.tempPassword, - ); + expect(config.getAuthentication()).to.be.eql(defaultSettings.authentication[0]); + expect(config.getTempPasswordConfig()).to.be.eql(defaultSettings.tempPassword); }); afterEach(function () { diff --git a/test/testLogin.test.js b/test/testLogin.test.js index 13c266e1..2fe614c5 100644 --- a/test/testLogin.test.js +++ b/test/testLogin.test.js @@ -20,13 +20,13 @@ describe('auth', async () => { describe('test login / logout', async function () { // Test to get all students record it('should get 401 not logged in', async function () { - const res = await chai.request(app).get('/auth/profile'); + const res = await chai.request(app).get('/api/auth/profile'); res.should.have.status(401); }); it('should be able to login', async function () { - const res = await chai.request(app).post('/auth/login').send({ + const res = await chai.request(app).post('/api/auth/login').send({ username: 'admin', password: 'admin', }); @@ -43,26 +43,17 @@ describe('auth', async () => { }); it('should now be able to access the profile', async function () { - const res = await chai - .request(app) - .get('/auth/profile') - .set('Cookie', `${cookie}`); + const res = await chai.request(app).get('/api/auth/profile').set('Cookie', `${cookie}`); res.should.have.status(200); }); it('should now be able to logout', async function () { - const res = await chai - .request(app) - .post('/auth/logout') - .set('Cookie', `${cookie}`); + const res = await chai.request(app).post('/api/auth/logout').set('Cookie', `${cookie}`); res.should.have.status(200); }); it('test cannot access profile page', async function () { - const res = await chai - .request(app) - .get('/auth/profile') - .set('Cookie', `${cookie}`); + const res = await chai.request(app).get('/api/auth/profile').set('Cookie', `${cookie}`); res.should.have.status(401); }); diff --git a/test/testPluginLoader.js b/test/testPluginLoader.test.js similarity index 76% rename from test/testPluginLoader.js rename to test/testPluginLoader.test.js index 0c52209a..ae6bbcf6 100644 --- a/test/testPluginLoader.js +++ b/test/testPluginLoader.test.js @@ -1,4 +1,3 @@ -/* eslint-disable max-len */ const originalEnv = process.env; const chai = require('chai'); const plugin = require('../src/plugin'); @@ -9,15 +8,12 @@ const expect = chai.expect; describe('creating a new PluginLoader and loading plugins', function () { before(function () { - process.env.GITPROXY_PLUGIN_FILES = - './packages/git-proxy-notify-hello/index.js'; + process.env.GITPROXY_PLUGIN_FILES = './packages/git-proxy-notify-hello/index.js'; }); it('should load file-based plugins when set from env var', async function () { plugin.createLoader().then((loader) => { - expect(loader.paths).to.eql([ - './packages/git-proxy-notify-hello/index.js', - ]); + expect(loader.paths).to.eql(['./packages/git-proxy-notify-hello/index.js']); expect(loader.names).to.be.empty; expect(loader.plugins.length).to.equal(1); expect(loader.plugins[0]) diff --git a/test/testPush.test.js b/test/testPush.test.js index c046edd4..f4e09a4a 100644 --- a/test/testPush.test.js +++ b/test/testPush.test.js @@ -16,7 +16,7 @@ describe('auth', async () => { app = await service.start(); await db.deleteUser('login-test-user'); - const res = await chai.request(app).post('/auth/login').send({ + const res = await chai.request(app).post('/api/auth/login').send({ username: 'admin', password: 'admin', }); @@ -45,10 +45,7 @@ describe('auth', async () => { }); after(async function () { - const res = await chai - .request(app) - .post('/auth/logout') - .set('Cookie', `${cookie}`); + const res = await chai.request(app).post('/api/auth/logout').set('Cookie', `${cookie}`); res.should.have.status(200); await service.httpServer.close(); diff --git a/test/testUserCreation.test.js b/test/testUserCreation.test.js index 1d6f35d2..de5238e8 100644 --- a/test/testUserCreation.test.js +++ b/test/testUserCreation.test.js @@ -1,7 +1,7 @@ // Import the dependencies for testing const chai = require('chai'); +const bcrypt = require('bcrypt'); const chaiHttp = require('chai-http'); -const passwordHash = require('password-hash'); const db = require('../src/db'); const service = require('../src/service'); @@ -29,7 +29,7 @@ describe('user creation', async () => { }); it('should be able to login', async function () { - const res = await chai.request(app).post('/auth/login').send({ + const res = await chai.request(app).post('/api/auth/login').send({ username: 'admin', password: 'admin', }); @@ -40,29 +40,17 @@ describe('user creation', async () => { }); it('should be able to create a new user', async function () { - const res = await chai - .request(app) - .post('/auth/profile') - .set('Cookie', `${cookie}`) - .send({ - username: 'login-test-user', - email: 'paul.timothy.groves@gmail.com', - gitAccount: 'test123', - admin: true, - }); + const res = await chai.request(app).post('/api/auth/profile').set('Cookie', `${cookie}`).send({ + username: 'login-test-user', + email: 'paul.timothy.groves@gmail.com', + gitAccount: 'test123', + admin: true, + }); res.should.have.status(200); - }); - - it('new users should be marked to change next login', async function () { - const user = await db.findUser('login-test-user'); - user.changePassword.should.be.true; - }); + }).skip(); it('logout', async function () { - const res = await chai - .request(app) - .post('/auth/logout') - .set('Cookie', `${cookie}`); + const res = await chai.request(app).post('/api/auth/logout').set('Cookie', `${cookie}`); res.should.have.status(200); }); @@ -70,63 +58,25 @@ describe('user creation', async () => { // we don't know the users tempoary password - so force update a // pasword const user = await db.findUser('login-test-user'); - user.password = passwordHash.generate('test1234'); - await db.updateUser(user); - const res = await chai.request(app).post('/auth/login').send({ - username: 'login-test-user', - password: 'test1234', - }); + await bcrypt.hash('test1234', 10, async function (err, hash) { + user.password = hash; - expect(res).to.have.cookie('connect.sid'); - res.should.have.status(200); - setCookie(res); - }); + await db.updateUser(user); - it('change the password', async function () { - const res = await chai - .request(app) - .post('/auth/password') - .set('Cookie', `${cookie}`) - .send({ - oldPassword: 'test1234', - newPassword: 'testabcd', - }); - - res.should.have.status(200); - }); - - it('updated users should NOT need to change next login', async function () { - const user = await db.findUser('login-test-user'); - user.changePassword.should.be.false; - }); - - it('logout - again', function (done) { - chai - .request(app) - .post('/auth/logout') - .end((err, res) => { - res.should.have.status(200); - done(); + const res = await chai.request(app).post('/api/auth/login').send({ + username: 'login-test-user', + password: 'test1234', }); - }); - it('login again', async function () { - const res = await chai.request(app).post('/auth/login').send({ - username: 'login-test-user', - password: 'testabcd', + expect(res).to.have.cookie('connect.sid'); + res.should.have.status(200); + setCookie(res); }); - - expect(res).to.have.cookie('connect.sid'); - res.should.have.status(200); - setCookie(res); }); it('should access the profile', async function () { - const res = await chai - .request(app) - .get('/auth/profile') - .set('Cookie', `${cookie}`); + const res = await chai.request(app).get('/api/auth/profile').set('Cookie', `${cookie}`); res.should.have.status(200); res.body.username.should.equal('login-test-user'); diff --git a/user-settings.json b/user-settings.json deleted file mode 100644 index 1d958b6c..00000000 --- a/user-settings.json +++ /dev/null @@ -1 +0,0 @@ -{ "authorisedList": [ { "project": "foo", "name": "bar", "url": "https://github.com/foo/bar.git" } ] } diff --git a/website/docs/quickstart/approve.mdx b/website/docs/quickstart/approve.mdx index 99409a84..f1484150 100644 --- a/website/docs/quickstart/approve.mdx +++ b/website/docs/quickstart/approve.mdx @@ -9,19 +9,19 @@ All pushes that flow through Git Proxy require an approval (authorisation). Unti ### Prerequisites -- Proxy and REST API are running ([default behaviour](https://github.com/finos/git-proxy/blob/main/index.js)) -- Proxy and REST API are running on `localhost:8000` and `localhost:8080`, respectively -- [Intercepting a push](/docs/quickstart/intercept) instructions have been followed and you've reached [Push via Git Proxy](/docs/quickstart/intercept#push-via-git-proxy) -- [`curl`](https://curl.se/) is installed +- [x] Proxy and REST API are running ([default behaviour](https://github.com/finos/git-proxy/blob/main/index.js)) +- [x] Proxy and REST API are running on `localhost:8000` and `localhost:8080`, respectively +- [x] [Intercepting a push](/docs/quickstart/intercept) instructions have been followed and you've reached [Push via Git Proxy](/docs/quickstart/intercept#push-via-git-proxy) +- [x] [`curl`](https://curl.se/) is installed ### Instructions #### 1. Find the tracking `ID` -Following on from [Push via Git Proxy](/docs/quickstart/intercept#push-via-git-proxy), you'll receive a unique URL: +Following on from [Push via Git Proxy](/docs/quickstart/intercept#push-via-git-proxy), a unique & shareable link is generated: ``` -http://localhost:8080/requests/0000000000000000000000000000000000000000__79b4d8953cbc324bcc1eb53d6412ff89666c241f +http://localhost:8080/admin/push/0000000000000000000000000000000000000000__79b4d8953cbc324bcc1eb53d6412ff89666c241f ``` The `ID` for your push corresponds to the last part of the URL: @@ -60,7 +60,7 @@ curl -b ./git-proxy-cookie \ #### 5. Re-push your code -Execute `git push` to send your approved code through Git Proxy to the upstream repository: +Execute `git push` to send your approved code to the upstream repository: ```bash $ git push @@ -141,8 +141,61 @@ Logout: OK ## Using the UI -:::note +### Prerequisites + +- [x] Proxy and REST API are running ([default behaviour](https://github.com/finos/git-proxy/blob/main/index.js)) +- [x] Proxy and REST API are running on `localhost:8000` and `localhost:8080`, respectively +- [x] UI is running on `localhost:3000` + +### Instructions + +#### 1. Login as administrator + +[Login](http://localhost:3000/login) to the dashboard. As the UI is running on port `3000`, the URL is: + +```bash +http://localhost:3000/login +``` + +Fill in the form using the following credentials: + +``` +Username: admin +Password: admin +``` + +Once submitted, you should be logged in as administrator 💪 + +#### 2. Visit the shareable link -The web UI is under active development. Keep an eye out for updates in our latest [releases](https://github.com/finos/git-proxy/releases). +Following on from [Push via Git Proxy](/docs/quickstart/intercept#push-via-git-proxy), a unique & shareable link is generated: -::: \ No newline at end of file +```bash +remote: Git Proxy has received your push ✅ +remote: +remote: 🔗 Shareable Link +remote: http://localhost:8080/admin/push/000000__b12557 +``` + +Insert the URL directly into your web browser. + +#### 3. Approve the push using the dashboard + +Press the approve button and a modal will appear. Happy with the contents of the push? 🤗 + +In the modal, check the tickbox ✅ and press the approve button. + +#### 4. Re-push your code + +Execute `git push` to send your approved code to the upstream repository: + +```bash +$ git push +Enumerating objects: 5, done. +Counting objects: 100% (5/5), done. +Delta compression using up to 10 threads +Compressing objects: 100% (3/3), done. +Writing objects: 100% (3/3), 470 bytes | 470.00 KiB/s, done. +Total 3 (delta 2), reused 0 (delta 0), pack-reused 0 +remote: Resolving deltas: 100% (2/2), completed with 2 local objects. +``` diff --git a/website/docs/quickstart/intercept.mdx b/website/docs/quickstart/intercept.mdx index 4b2f40f8..7a55c039 100644 --- a/website/docs/quickstart/intercept.mdx +++ b/website/docs/quickstart/intercept.mdx @@ -46,7 +46,6 @@ Listening on 8000 Service Listening on 8080 ``` - ### Introduce Git Proxy to your clone Navigate into your test-bed repository (where you cloned your Git Proxy fork) on your PC: @@ -90,15 +89,15 @@ Immediately after a push, you should receive a message in your terminal: ```bash remote: -remote: Git Proxy has received your push: -remote: -remote: http://localhost:8080/requests/000000__b12557 +remote: Git Proxy has received your push ✅ +remote: +remote: 🔗 Shareable Link +remote: http://localhost:8080/admin/push/000000__b12557 remote: ``` The push is now held in a suspended state by Git Proxy and requires [approval](/docs/quickstart/approve) before it can be pushed to the upstream repository on GitHub. - #### Managing credentials Git Proxy will prompt the entry of your git credentials. These credentials are your GitHub username and a [Personal Access Token](https://github.com/settings/tokens). For the ability to push and pull code through Git Proxy, you will only require the `public_repo` scope. @@ -118,6 +117,3 @@ git config --global credential.helper store # Linux Git Proxy **does not** use your Personal Access Token other than to authenticate with GitHub when pushing code. This is identical to the process of pushing code to a repository without Git Proxy installed. ::: - - - diff --git a/website/src/pages/index.js b/website/src/pages/index.js index a5c3bf6a..7d9d5141 100644 --- a/website/src/pages/index.js +++ b/website/src/pages/index.js @@ -1,4 +1,3 @@ -/* eslint-disable max-len */ import React from 'react'; import Layout from '@theme/Layout'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; @@ -13,143 +12,131 @@ function Home() { const { siteConfig = {} } = context; return ( -
-
-

- Git Proxy v{siteConfig.customFields.version} is out! 🥳 -

+
+
+

Git Proxy v{siteConfig.customFields.version} is out! 🥳

{siteConfig.tagline}

-
-
-
+
+
-
-
-
+
+
+
-
-
$ git push
+
+
$ git push

- + You have configured the following push protections and policies:

-
+
- + {' '} Apache-2.0, MIT license(s) only
-
+
- + {' '} No secrets detected
-
+
- + {' '} No data files found (.csv, .txt, .log)
-
+
- + {' '} - - Author e-mail address does not match domain - + Author e-mail address does not match domain
-
+
- + {' '} Your push has been blocked... - +
-
-

Meet the Team

-
-
-
- +
+

Meet the Team

+
+
+
+
-
-
+
+
{' '}
-
-
+
+
{' '} - +
-
-
+
+
{' '}