diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..7a3d61558 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +node_modules +built +healthcheck.js diff --git a/.github/resources/docker-compose.be-test.yml b/.github/resources/docker-compose.be-test.yml new file mode 100644 index 000000000..6efdd87f6 --- /dev/null +++ b/.github/resources/docker-compose.be-test.yml @@ -0,0 +1,30 @@ +version: '3' + +services: + backend: + container_name: infisical-backend-test + restart: unless-stopped + depends_on: + - mongo + image: infisical/backend:test + command: npm run start + environment: + - NODE_ENV=production + - MONGO_URL=mongodb://test:example@mongo:27017/?authSource=admin + - MONGO_USERNAME=test + - MONGO_PASSWORD=example + networks: + - infisical-test + + mongo: + container_name: infisical-mongo-test + image: mongo + restart: always + environment: + - MONGO_INITDB_ROOT_USERNAME=test + - MONGO_INITDB_ROOT_PASSWORD=example + networks: + - infisical-test + +networks: + infisical-test: diff --git a/.github/resources/healthcheck.sh b/.github/resources/healthcheck.sh new file mode 100755 index 000000000..bc28e3607 --- /dev/null +++ b/.github/resources/healthcheck.sh @@ -0,0 +1,26 @@ +# Name of the target container to check +container_name="$1" +# Timeout in seconds. Default: 60 +timeout=$((${2:-60})); + +if [ -z $container_name ]; then + echo "No container name specified"; + exit 1; +fi + +echo "Container: $container_name"; +echo "Timeout: $timeout sec"; + +try=0; +is_healthy="false"; +while [ $is_healthy != "true" ]; +do + try=$(($try + 1)); + printf "■"; + is_healthy=$(docker inspect --format='{{json .State.Health}}' $container_name | jq '.Status == "healthy"'); + sleep 1; + if [[ $try -eq $timeout ]]; then + echo " Container was not ready within timeout"; + exit 1; + fi +done diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index e85ddcfd5..1e836a178 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -3,40 +3,38 @@ name: Push to Docker Hub on: [workflow_dispatch] jobs: - backend-image: name: Build backend image runs-on: ubuntu-latest steps: - - - name: ☁️ Checkout source + - name: ☁️ Checkout source uses: actions/checkout@v3 - - - name: 🔧 Set up QEMU + - name: 🔧 Set up QEMU uses: docker/setup-qemu-action@v2 - - - name: 🔧 Set up Docker Buildx + - name: 🔧 Set up Docker Buildx uses: docker/setup-buildx-action@v2 - - - name: 🐋 Login to Docker Hub + - name: 🐋 Login to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - # - - # name: 📦 Build backend and export to Docker - # uses: docker/build-push-action@v3 - # with: - # load: true - # context: backend - # tags: infisical/backend:test - # - - # name: 🧪 Test backend image - # run: | - # docker run --rm infisical/backend:test - - - name: 🏗️ Build backend and push + - name: 📦 Build backend and export to Docker + uses: docker/build-push-action@v3 + with: + load: true + context: backend + tags: infisical/backend:test + - name: ⏻ Spawn backend container and dependencies + run: | + docker compose -f .github/resources/docker-compose.be-test.yml up --wait --quiet-pull + - name: 🧪 Test backend image + run: | + ./.github/resources/healthcheck.sh infisical-backend-test + - name: ⏻ Shut down backend container and dependencies + run: | + docker compose -f .github/resources/docker-compose.be-test.yml down + - name: 🏗️ Build backend and push uses: docker/build-push-action@v3 with: push: true @@ -44,42 +42,40 @@ jobs: tags: infisical/backend:latest platforms: linux/amd64,linux/arm64 - frontend-image: name: Build frontend image runs-on: ubuntu-latest steps: - - - name: ☁️ Checkout source + - name: ☁️ Checkout source uses: actions/checkout@v3 - - - name: 🔧 Set up QEMU + - name: 🔧 Set up QEMU uses: docker/setup-qemu-action@v2 - - - name: 🔧 Set up Docker Buildx + - name: 🔧 Set up Docker Buildx uses: docker/setup-buildx-action@v2 - - - name: 🐋 Login to Docker Hub + - name: 🐋 Login to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - # - - # name: 📦 Build frontend and export to Docker - # uses: docker/build-push-action@v3 - # with: - # load: true - # context: frontend - # tags: infisical/frontend:test - # build-args: | - # POSTHOG_API_KEY=${{ secrets.PUBLIC_POSTHOG_API_KEY }} - # - - # name: 🧪 Test frontend image - # run: | - # docker run --rm infisical/frontend:test - - - name: 🏗️ Build frontend and push + - name: 📦 Build frontend and export to Docker + uses: docker/build-push-action@v3 + with: + load: true + context: frontend + tags: infisical/frontend:test + build-args: | + POSTHOG_API_KEY=${{ secrets.PUBLIC_POSTHOG_API_KEY }} + - name: ⏻ Spawn frontend container + run: | + docker run -d --rm --name infisical-frontend-test infisical/frontend:test + - name: 🧪 Test frontend image + run: | + ./.github/resources/healthcheck.sh infisical-frontend-test + - name: ⏻ Shut down frontend container + run: | + docker stop infisical-frontend-test + - name: 🏗️ Build frontend and push uses: docker/build-push-action@v3 with: push: true diff --git a/backend/Dockerfile b/backend/Dockerfile index 2cc270947..ccc76e66e 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -2,11 +2,14 @@ FROM node:16-bullseye-slim WORKDIR /app -COPY package*.json . +COPY package.json package-lock.json ./ -RUN npm install +RUN npm ci --only-production COPY . . -CMD ["npm", "run", "start"] +HEALTHCHECK --interval=10s --timeout=3s --start-period=10s \ + CMD node healthcheck.js + +CMD ["npm", "run", "start"] diff --git a/backend/healthcheck.js b/backend/healthcheck.js new file mode 100644 index 000000000..8cb3dfcaa --- /dev/null +++ b/backend/healthcheck.js @@ -0,0 +1,24 @@ +const http = require('http'); +const PORT = process.env.PORT || 4000; +const options = { + host: 'localhost', + port: PORT, + timeout: 2000, + path: '/healthcheck' +}; + +const healthCheck = http.request(options, (res) => { + console.log(`HEALTHCHECK STATUS: ${res.statusCode}`); + if (res.statusCode == 200) { + process.exit(0); + } else { + process.exit(1); + } +}); + +healthCheck.on('error', function (err) { + console.error(`HEALTH CHECK ERROR: ${err}`); + process.exit(1); +}); + +healthCheck.end(); diff --git a/backend/package-lock.json b/backend/package-lock.json index d4c3d36a6..c13f138d4 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@godaddy/terminus": "^4.11.2", "@sentry/node": "^7.14.0", "@sentry/tracing": "^7.19.0", "@types/crypto-js": "^4.1.1", @@ -2028,6 +2029,14 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@godaddy/terminus": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@godaddy/terminus/-/terminus-4.11.2.tgz", + "integrity": "sha512-e/kbOWpGKME42eltM/wXM3RxSUOrfureZxEd6Dt6NXyFoJ7E8lnmm7znXydJsL3B7ky4HRFZI+eHrep54NZbeQ==", + "dependencies": { + "stoppable": "^1.1.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.7", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", @@ -6731,7 +6740,129 @@ "treeverse", "validate-npm-package-name", "which", - "write-file-atomic" + "write-file-atomic", + "@colors/colors", + "@gar/promisify", + "@npmcli/disparity-colors", + "@npmcli/git", + "@npmcli/installed-package-contents", + "@npmcli/metavuln-calculator", + "@npmcli/move-file", + "@npmcli/name-from-folder", + "@npmcli/node-gyp", + "@npmcli/promise-spawn", + "@npmcli/query", + "@tootallnate/once", + "agent-base", + "agentkeepalive", + "aggregate-error", + "ansi-regex", + "ansi-styles", + "aproba", + "are-we-there-yet", + "asap", + "balanced-match", + "bin-links", + "binary-extensions", + "brace-expansion", + "builtins", + "cidr-regex", + "clean-stack", + "clone", + "cmd-shim", + "color-convert", + "color-name", + "color-support", + "common-ancestor-path", + "concat-map", + "console-control-strings", + "cssesc", + "debug", + "debuglog", + "defaults", + "delegates", + "depd", + "dezalgo", + "diff", + "emoji-regex", + "encoding", + "env-paths", + "err-code", + "fs.realpath", + "function-bind", + "gauge", + "has", + "has-flag", + "has-unicode", + "http-cache-semantics", + "http-proxy-agent", + "https-proxy-agent", + "humanize-ms", + "iconv-lite", + "ignore-walk", + "imurmurhash", + "indent-string", + "infer-owner", + "inflight", + "inherits", + "ip", + "ip-regex", + "is-core-module", + "is-fullwidth-code-point", + "is-lambda", + "isexe", + "json-stringify-nice", + "jsonparse", + "just-diff", + "just-diff-apply", + "lru-cache", + "minipass-collect", + "minipass-fetch", + "minipass-flush", + "minipass-json-stream", + "minipass-sized", + "minizlib", + "mute-stream", + "negotiator", + "normalize-package-data", + "npm-bundled", + "npm-normalize-package-bin", + "npm-packlist", + "once", + "path-is-absolute", + "postcss-selector-parser", + "promise-all-reject-late", + "promise-call-limit", + "promise-inflight", + "promise-retry", + "promzard", + "read-cmd-shim", + "readable-stream", + "retry", + "safe-buffer", + "safer-buffer", + "set-blocking", + "signal-exit", + "smart-buffer", + "socks", + "socks-proxy-agent", + "spdx-correct", + "spdx-exceptions", + "spdx-expression-parse", + "spdx-license-ids", + "string_decoder", + "string-width", + "strip-ansi", + "supports-color", + "unique-filename", + "unique-slug", + "util-deprecate", + "validate-npm-package-license", + "walk-up-path", + "wcwidth", + "wide-align", + "wrappy", + "yallist" ], "dev": true, "dependencies": { @@ -10192,6 +10323,15 @@ "node": ">= 0.8" } }, + "node_modules/stoppable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", + "engines": { + "node": ">=4", + "npm": ">=6" + } + }, "node_modules/strict-uri-encode": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", @@ -12607,6 +12747,14 @@ "strip-json-comments": "^3.1.1" } }, + "@godaddy/terminus": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@godaddy/terminus/-/terminus-4.11.2.tgz", + "integrity": "sha512-e/kbOWpGKME42eltM/wXM3RxSUOrfureZxEd6Dt6NXyFoJ7E8lnmm7znXydJsL3B7ky4HRFZI+eHrep54NZbeQ==", + "requires": { + "stoppable": "^1.1.0" + } + }, "@humanwhocodes/config-array": { "version": "0.11.7", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", @@ -18631,6 +18779,11 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, + "stoppable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==" + }, "strict-uri-encode": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", diff --git a/backend/package.json b/backend/package.json index 7e0dc7ad9..32ca68b19 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,5 +1,6 @@ { "dependencies": { + "@godaddy/terminus": "^4.11.2", "@sentry/node": "^7.14.0", "@sentry/tracing": "^7.19.0", "@types/crypto-js": "^4.1.1", diff --git a/backend/src/controllers/authController.ts b/backend/src/controllers/authController.ts index eb537f2b9..20ac813d4 100644 --- a/backend/src/controllers/authController.ts +++ b/backend/src/controllers/authController.ts @@ -103,7 +103,7 @@ export const login2 = async (req: Request, res: Response) => { // store (refresh) token in httpOnly cookie res.cookie('jid', tokens.refreshToken, { httpOnly: true, - path: '/token', + path: '/', sameSite: 'strict', secure: NODE_ENV === 'production' ? true : false }); @@ -147,7 +147,7 @@ export const logout = async (req: Request, res: Response) => { // clear httpOnly cookie res.cookie('jid', '', { httpOnly: true, - path: '/token', + path: '/', sameSite: 'strict', secure: NODE_ENV === 'production' ? true : false }); diff --git a/backend/src/helpers/rateLimiter.ts b/backend/src/helpers/rateLimiter.ts index a0788246a..6153369e3 100644 --- a/backend/src/helpers/rateLimiter.ts +++ b/backend/src/helpers/rateLimiter.ts @@ -2,34 +2,35 @@ import rateLimit from 'express-rate-limit'; // 300 requests per 15 minutes const apiLimiter = rateLimit({ - windowMs: 15 * 60 * 1000, - max: 400, - standardHeaders: true, - legacyHeaders: false + windowMs: 15 * 60 * 1000, + max: 400, + standardHeaders: true, + legacyHeaders: false, + skip: (request) => request.path === '/healthcheck' }); // 5 requests per hour const signupLimiter = rateLimit({ - windowMs: 60 * 60 * 1000, - max: 10, - standardHeaders: true, - legacyHeaders: false + windowMs: 60 * 60 * 1000, + max: 10, + standardHeaders: true, + legacyHeaders: false }); // 10 requests per hour const loginLimiter = rateLimit({ - windowMs: 60 * 60 * 1000, - max: 20, - standardHeaders: true, - legacyHeaders: false + windowMs: 60 * 60 * 1000, + max: 20, + standardHeaders: true, + legacyHeaders: false }); // 5 requests per hour const passwordLimiter = rateLimit({ - windowMs: 60 * 60 * 1000, - max: 10, - standardHeaders: true, - legacyHeaders: false + windowMs: 60 * 60 * 1000, + max: 10, + standardHeaders: true, + legacyHeaders: false }); export { apiLimiter, signupLimiter, loginLimiter, passwordLimiter }; diff --git a/backend/src/index.ts b/backend/src/index.ts index 61872184c..6abc65420 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -1,3 +1,5 @@ +/* eslint-disable no-console */ +import http from 'http'; import express from 'express'; import helmet from 'helmet'; import cors from 'cors'; @@ -7,16 +9,17 @@ import dotenv from 'dotenv'; dotenv.config(); import * as Sentry from '@sentry/node'; -import { PORT, SENTRY_DSN, NODE_ENV, MONGO_URL, SITE_URL, POSTHOG_PROJECT_API_KEY, POSTHOG_HOST, TELEMETRY_ENABLED } from './config'; +import { PORT, SENTRY_DSN, NODE_ENV, MONGO_URL, SITE_URL } from './config'; import { apiLimiter } from './helpers/rateLimiter'; +import { createTerminus } from '@godaddy/terminus'; const app = express(); Sentry.init({ - dsn: SENTRY_DSN, - tracesSampleRate: 1.0, - debug: NODE_ENV === 'production' ? false : true, - environment: NODE_ENV + dsn: SENTRY_DSN, + tracesSampleRate: 1.0, + debug: NODE_ENV === 'production' ? false : true, + environment: NODE_ENV }); import { @@ -40,31 +43,35 @@ import { } from './routes'; const connectWithRetry = () => { - mongoose.connect(MONGO_URL) - .then(() => console.log('Successfully connected to DB')) - .catch((e) => { - console.log('Failed to connect to DB ', e); - setTimeout(() => { - console.log(e); - }, 5000); - }); -} + mongoose + .connect(MONGO_URL) + .then(() => console.log('Successfully connected to DB')) + .catch((e) => { + console.log('Failed to connect to DB ', e); + setTimeout(() => { + console.log(e); + }, 5000); + }); + return mongoose.connection; +}; -connectWithRetry(); +const dbConnection = connectWithRetry(); app.enable('trust proxy'); app.use(cookieParser()); -app.use(cors({ - credentials: true, - origin: SITE_URL -})); +app.use( + cors({ + credentials: true, + origin: SITE_URL + }) +); if (NODE_ENV === 'production') { - // enable app-wide rate-limiting + helmet security - // in production - app.disable('x-powered-by'); - app.use(apiLimiter); - app.use(helmet()); + // enable app-wide rate-limiting + helmet security + // in production + app.disable('x-powered-by'); + app.use(apiLimiter); + app.use(helmet()); } app.use(express.json()); @@ -88,6 +95,35 @@ app.use('/api/v1/stripe', stripeRouter); app.use('/api/v1/integration', integrationRouter); app.use('/api/v1/integration-auth', integrationAuthRouter); -app.listen(PORT, () => { - console.log('Listening on PORT ' + PORT); +const server = http.createServer(app); + +const onSignal = () => { + console.log('Server is starting clean-up'); + return Promise.all([ + () => { + dbConnection.close(() => { + console.info('Database connection closed'); + }); + } + ]); +}; + +const healthCheck = () => { + // `state.isShuttingDown` (boolean) shows whether the server is shutting down or not + return Promise + .resolve + // optionally include a resolve value to be included as + // info in the health check response + (); +}; + +createTerminus(server, { + healthChecks: { + '/healthcheck': healthCheck, + onSignal + } +}); + +server.listen(PORT, () => { + console.log('Listening on PORT ' + PORT); }); diff --git a/backend/src/models/token.ts b/backend/src/models/token.ts index 9bf8836f5..9569aee0b 100644 --- a/backend/src/models/token.ts +++ b/backend/src/models/token.ts @@ -2,25 +2,30 @@ import { Schema, model } from 'mongoose'; import { EMAIL_TOKEN_LIFETIME } from '../config'; export interface IToken { - email: String; - token: String; - createdAt: Date; + email: string; + token: string; + createdAt: Date; } const tokenSchema = new Schema({ - email: { - type: String, - required: true - }, - token: { - type: String, - required: true - }, - createdAt: { - type: Date, - expires: parseInt(EMAIL_TOKEN_LIFETIME), - default: Date.now - } + email: { + type: String, + required: true + }, + token: { + type: String, + required: true + }, + createdAt: { + type: Date, + default: Date.now + } +}); + +tokenSchema.index({ + createdAt: 1 +}, { + expireAfterSeconds: parseInt(EMAIL_TOKEN_LIFETIME) }); const Token = model('Token', tokenSchema); diff --git a/backend/src/services/PostHogClient.ts b/backend/src/services/PostHogClient.ts index eaf56aa6a..4096ac8ec 100644 --- a/backend/src/services/PostHogClient.ts +++ b/backend/src/services/PostHogClient.ts @@ -1,15 +1,19 @@ import { PostHog } from 'posthog-node'; -import { NODE_ENV, POSTHOG_HOST, POSTHOG_PROJECT_API_KEY, TELEMETRY_ENABLED } from '../config'; +import { + NODE_ENV, + POSTHOG_HOST, + POSTHOG_PROJECT_API_KEY, + TELEMETRY_ENABLED +} from '../config'; + +console.log('TELEMETRY_ENABLED: ', TELEMETRY_ENABLED); let postHogClient: any; -if ( - NODE_ENV === 'production' - && TELEMETRY_ENABLED -) { - // case: enable opt-out telemetry in production - postHogClient = new PostHog(POSTHOG_PROJECT_API_KEY, { - host: POSTHOG_HOST - }); +if (NODE_ENV === 'production' && TELEMETRY_ENABLED) { + // case: enable opt-out telemetry in production + postHogClient = new PostHog(POSTHOG_PROJECT_API_KEY, { + host: POSTHOG_HOST + }); } -export default postHogClient; \ No newline at end of file +export default postHogClient; diff --git a/cli/packages/cmd/export.go b/cli/packages/cmd/export.go index a0d89301f..f25a726e7 100644 --- a/cli/packages/cmd/export.go +++ b/cli/packages/cmd/export.go @@ -19,6 +19,7 @@ const ( FormatDotenv string = "dotenv" FormatJson string = "json" FormatCSV string = "csv" + FormatYaml string = "yaml" ) // exportCmd represents the export command @@ -101,8 +102,10 @@ func formatEnvs(envs []models.SingleEnvironmentVariable, format string) (string, return formatAsJson(envs), nil case FormatCSV: return formatAsCSV(envs), nil + case FormatYaml: + return formatAsYaml(envs), nil default: - return "", fmt.Errorf("invalid format flag: %s", format) + return "", fmt.Errorf("invalid format type: %s. Available format types are [%s]", format, []string{FormatDotenv, FormatJson, FormatCSV, FormatYaml}) } } @@ -127,6 +130,14 @@ func formatAsDotEnv(envs []models.SingleEnvironmentVariable) string { return dotenv } +func formatAsYaml(envs []models.SingleEnvironmentVariable) string { + var dotenv string + for _, env := range envs { + dotenv += fmt.Sprintf("%s: %s\n", env.Key, env.Value) + } + return dotenv +} + // Format environment variables as a JSON file func formatAsJson(envs []models.SingleEnvironmentVariable) string { // Dump as a json array diff --git a/cli/packages/cmd/root.go b/cli/packages/cmd/root.go index 99f7d384d..7317b3eea 100644 --- a/cli/packages/cmd/root.go +++ b/cli/packages/cmd/root.go @@ -15,7 +15,7 @@ var rootCmd = &cobra.Command{ Short: "Infisical CLI is used to inject environment variables into any process", Long: `Infisical is a simple, end-to-end encrypted service that enables teams to sync and manage their environment variables across their development life cycle.`, CompletionOptions: cobra.CompletionOptions{DisableDefaultCmd: true}, - Version: "0.1.10", + Version: "0.1.11", } // Execute adds all child commands to the root command and sets flags appropriately. diff --git a/docker-compose.yml b/docker-compose.yml index cc92e1406..bd9022cef 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,7 +15,7 @@ services: - backend networks: - infisical - + backend: container_name: infisical-backend restart: unless-stopped @@ -28,7 +28,7 @@ services: - NODE_ENV=production networks: - infisical - + frontend: container_name: infisical-frontend restart: unless-stopped diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 5e59c68aa..5deb468f7 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -60,5 +60,8 @@ EXPOSE 3000 ENV PORT 3000 ENV NEXT_TELEMETRY_DISABLED 1 +HEALTHCHECK --interval=10s --timeout=3s --start-period=10s \ + CMD node scripts/healthcheck.js + CMD ["/app/scripts/start.sh"] diff --git a/frontend/components/basic/InputField.tsx b/frontend/components/basic/InputField.tsx index 141578214..46b42c15c 100644 --- a/frontend/components/basic/InputField.tsx +++ b/frontend/components/basic/InputField.tsx @@ -1,5 +1,4 @@ import React, { useState } from 'react'; -import { useRouter } from 'next/router'; import { faCircle, faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -26,7 +25,6 @@ const InputField = ( Pick ) => { const [passwordVisible, setPasswordVisible] = useState(false); - const router = useRouter(); if (props.static === true) { return ( diff --git a/frontend/components/basic/Layout.tsx b/frontend/components/basic/Layout.tsx index 5057f70ce..56a16cb3d 100644 --- a/frontend/components/basic/Layout.tsx +++ b/frontend/components/basic/Layout.tsx @@ -41,7 +41,7 @@ interface LayoutProps { export default function Layout({ children }: LayoutProps) { const router = useRouter(); const [workspaceList, setWorkspaceList] = useState([]); - const [workspaceMapping, setWorkspaceMapping] = useState([{ 1: 2 }]); + const [workspaceMapping, setWorkspaceMapping] = useState([{ '1': '2' }]); const [workspaceSelected, setWorkspaceSelected] = useState('∞'); const [newWorkspaceName, setNewWorkspaceName] = useState(''); const [isOpen, setIsOpen] = useState(false); @@ -221,20 +221,20 @@ export default function Layout({ children }: LayoutProps) { useEffect(() => { try { if ( - workspaceMapping[Number(workspaceSelected)] && - `${workspaceMapping[Number(workspaceSelected)]}` !== + workspaceMapping[workspaceSelected as any] && + `${workspaceMapping[workspaceSelected as any]}` !== router.asPath .split('/') [router.asPath.split('/').length - 1].split('?')[0] ) { router.push( '/dashboard/' + - workspaceMapping[Number(workspaceSelected)] + + workspaceMapping[workspaceSelected as any] + '?Development' ); localStorage.setItem( 'projectData.id', - `${workspaceMapping[Number(workspaceSelected)]}` + `${workspaceMapping[workspaceSelected as any]}` ); } } catch (error) { diff --git a/frontend/components/dashboard/DropZone.tsx b/frontend/components/dashboard/DropZone.tsx index 5807e0f83..aef7dbeb3 100644 --- a/frontend/components/dashboard/DropZone.tsx +++ b/frontend/components/dashboard/DropZone.tsx @@ -63,13 +63,15 @@ const DropZone = ({ if (event.target === null || event.target.result === null) return; // parse function's argument looks like to be ArrayBuffer const keyPairs = parse(event.target.result as Buffer); - const newData = Object.keys(keyPairs).map((key, index) => [ - guidGenerator(), - numCurrentRows + index, - key, - keyPairs[key as keyof typeof keyPairs], - 'shared' - ]); + const newData = Object.keys(keyPairs).map((key, index) => { + return { + id: guidGenerator(), + pos: numCurrentRows + index, + key: key, + value: keyPairs[key as keyof typeof keyPairs], + type: 'shared' + }; + }); setData(newData); setButtonReady(true); }; @@ -97,13 +99,15 @@ const DropZone = ({ if (typeof result === 'string') { const newData = result .split('\n') - .map((line: string, index: number) => [ - guidGenerator(), - numCurrentRows + index, - line.split('=')[0], - line.split('=').slice(1, line.split('=').length).join('='), - 'shared' - ]); + .map((line: string, index: number) => { + return { + id: guidGenerator(), + pos: numCurrentRows + index, + key: line.split('=')[0], + value: line.split('=').slice(1, line.split('=').length).join('='), + type: 'shared' + }; + }); setData(newData); setButtonReady(true); } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 229fd05fc..5e3bb010b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -41,6 +41,7 @@ "react-redux": "^8.0.2", "react-table": "^7.8.0", "set-cookie-parser": "^2.5.1", + "sharp": "^0.31.2", "styled-components": "^5.3.5", "tweetnacl": "^1.0.3", "tweetnacl-util": "^0.15.1", @@ -2025,6 +2026,25 @@ "node": ">= 0.6.0" } }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -2034,6 +2054,16 @@ "node": ">=8" } }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2094,6 +2124,29 @@ "node": ">= 0.4.0" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/buffer-from": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz", @@ -2229,6 +2282,11 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, "node_modules/cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -2251,11 +2309,22 @@ "node": ">=6" } }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -2266,8 +2335,16 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } }, "node_modules/combined-stream": { "version": "1.0.8", @@ -2474,6 +2551,28 @@ "node": ">=0.10" } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2544,6 +2643,14 @@ "node": ">=6" } }, + "node_modules/detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "engines": { + "node": ">=8" + } + }, "node_modules/detective": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", @@ -2657,6 +2764,14 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.12.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", @@ -3421,6 +3536,14 @@ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "engines": { + "node": ">=6" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -3619,6 +3742,11 @@ "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3724,6 +3852,11 @@ "resolved": "https://registry.npmjs.org/github-buttons/-/github-buttons-2.22.0.tgz", "integrity": "sha512-N5bk01s1WgK1FVtoeSUVkRkJpkaSu8yHMPcjye+PTa0jsRjMRNrYqVLgpUf2RA5Kvec05DfHYAT6/68fwkdqPw==" }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + }, "node_modules/glob": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", @@ -3981,6 +4114,25 @@ "node": ">=8.0.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -4038,6 +4190,11 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, "node_modules/inline-style-parser": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", @@ -4057,6 +4214,11 @@ "node": ">= 0.4" } }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -4605,7 +4767,6 @@ "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, "dependencies": { "yallist": "^4.0.0" }, @@ -5205,6 +5366,17 @@ "node": ">= 0.6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5222,6 +5394,11 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", @@ -5255,6 +5432,11 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -5319,6 +5501,22 @@ } } }, + "node_modules/node-abi": { + "version": "3.30.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.30.0.tgz", + "integrity": "sha512-qWO5l3SCqbwQavymOmtTVuCWZE23++S+rxyoHjXqUmPyzRcaoI4lA2gO55/drddGnedAyjA7sk76SfQ5lfUMnw==", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.0.0.tgz", + "integrity": "sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==" + }, "node_modules/node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", @@ -5465,7 +5663,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -5768,6 +5965,31 @@ "rrweb-snapshot": "^1.1.14" } }, + "node_modules/prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5816,6 +6038,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -5896,6 +6127,28 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", @@ -6465,7 +6718,6 @@ "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -6498,6 +6750,28 @@ "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" }, + "node_modules/sharp": { + "version": "0.31.2", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.31.2.tgz", + "integrity": "sha512-DUdNVEXgS5A97cTagSLIIp8dUZ/lZtk78iNVZgHdHbx1qnQR7JAHY0BnXnwwH39Iw+VKhO08CTYhIg0p98vQ5Q==", + "hasInstallScript": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.1", + "node-addon-api": "^5.0.0", + "prebuild-install": "^7.1.1", + "semver": "^7.3.8", + "simple-get": "^4.0.1", + "tar-fs": "^2.1.1", + "tunnel-agent": "^0.6.0" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6533,6 +6807,57 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -6851,6 +7176,32 @@ "node": ">=6" } }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/text-segmentation": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", @@ -7028,6 +7379,17 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/tweetnacl": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", @@ -7363,8 +7725,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/xtend": { "version": "4.0.2", @@ -7378,8 +7739,7 @@ "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 + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { "version": "1.10.2", @@ -8796,12 +9156,27 @@ "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", "optional": true }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -8837,6 +9212,15 @@ "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==" }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "buffer-from": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz", @@ -8931,6 +9315,11 @@ } } }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -8950,11 +9339,19 @@ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" }, + "color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "requires": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + } + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -8962,8 +9359,16 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } }, "combined-stream": { "version": "1.0.8", @@ -9130,6 +9535,19 @@ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "requires": { + "mimic-response": "^3.1.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -9181,6 +9599,11 @@ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" }, + "detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" + }, "detective": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", @@ -9281,6 +9704,14 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, "enhanced-resolve": { "version": "5.12.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", @@ -9848,6 +10279,11 @@ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, + "expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==" + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -9997,6 +10433,11 @@ "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -10071,6 +10512,11 @@ "resolved": "https://registry.npmjs.org/github-buttons/-/github-buttons-2.22.0.tgz", "integrity": "sha512-N5bk01s1WgK1FVtoeSUVkRkJpkaSu8yHMPcjye+PTa0jsRjMRNrYqVLgpUf2RA5Kvec05DfHYAT6/68fwkdqPw==" }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + }, "glob": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", @@ -10269,6 +10715,11 @@ "requires-port": "^1.0.0" } }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, "ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -10310,6 +10761,11 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, "inline-style-parser": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", @@ -10326,6 +10782,11 @@ "side-channel": "^1.0.4" } }, + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, "is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -10713,7 +11174,6 @@ "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, "requires": { "yallist": "^4.0.0" } @@ -11065,6 +11525,11 @@ "mime-db": "1.52.0" } }, + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -11079,6 +11544,11 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, "mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", @@ -11103,6 +11573,11 @@ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" }, + "napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -11141,6 +11616,19 @@ "use-sync-external-store": "1.2.0" } }, + "node-abi": { + "version": "3.30.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.30.0.tgz", + "integrity": "sha512-qWO5l3SCqbwQavymOmtTVuCWZE23++S+rxyoHjXqUmPyzRcaoI4lA2gO55/drddGnedAyjA7sk76SfQ5lfUMnw==", + "requires": { + "semver": "^7.3.5" + } + }, + "node-addon-api": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.0.0.tgz", + "integrity": "sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==" + }, "node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", @@ -11247,7 +11735,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "requires": { "wrappy": "1" } @@ -11438,6 +11925,25 @@ "rrweb-snapshot": "^1.1.14" } }, + "prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "requires": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -11470,6 +11976,15 @@ "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.1.1.tgz", "integrity": "sha512-hrzC564QIl0r0vy4l6MvRLhafmUowhO/O3KgVSoXIbbA2Sz4j8HGpJc6T2cubRVwMwpdiG/vKGfhT4IixmKN9w==" }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -11521,6 +12036,24 @@ "safe-buffer": "^5.1.0" } }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==" + } + } + }, "react": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", @@ -11924,7 +12457,6 @@ "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, "requires": { "lru-cache": "^6.0.0" } @@ -11948,6 +12480,21 @@ "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" }, + "sharp": { + "version": "0.31.2", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.31.2.tgz", + "integrity": "sha512-DUdNVEXgS5A97cTagSLIIp8dUZ/lZtk78iNVZgHdHbx1qnQR7JAHY0BnXnwwH39Iw+VKhO08CTYhIg0p98vQ5Q==", + "requires": { + "color": "^4.2.3", + "detect-libc": "^2.0.1", + "node-addon-api": "^5.0.0", + "prebuild-install": "^7.1.1", + "semver": "^7.3.8", + "simple-get": "^4.0.1", + "tar-fs": "^2.1.1", + "tunnel-agent": "^0.6.0" + } + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -11974,6 +12521,29 @@ "object-inspect": "^1.9.0" } }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" + }, + "simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "requires": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "requires": { + "is-arrayish": "^0.3.1" + } + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -12189,6 +12759,29 @@ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true }, + "tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + }, "text-segmentation": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", @@ -12343,6 +12936,14 @@ } } }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "tweetnacl": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", @@ -12578,8 +13179,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "xtend": { "version": "4.0.2", @@ -12590,8 +13190,7 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yaml": { "version": "1.10.2", diff --git a/frontend/package.json b/frontend/package.json index 8842aa296..7f7926c88 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -44,6 +44,7 @@ "react-redux": "^8.0.2", "react-table": "^7.8.0", "set-cookie-parser": "^2.5.1", + "sharp": "^0.31.2", "styled-components": "^5.3.5", "tweetnacl": "^1.0.3", "tweetnacl-util": "^0.15.1", diff --git a/frontend/pages/api/auth/EmailVerifyOnPasswordReset.ts b/frontend/pages/api/auth/EmailVerifyOnPasswordReset.ts new file mode 100644 index 000000000..b3d5e3d55 --- /dev/null +++ b/frontend/pages/api/auth/EmailVerifyOnPasswordReset.ts @@ -0,0 +1,34 @@ +interface Props { + email: string; + code: string; +} + +/** + * This is the second part of the account recovery step (a user needs to verify their email). + * A user need to click on a button in a magic link page + * @param {object} obj + * @param {object} obj.email - email of a user that is trying to recover access to their account + * @param {object} obj.code - token that a use received via the magic link + * @returns + */ +const EmailVerifyOnPasswordReset = async ({ email, code }: Props) => { + const response = await fetch('/api/v1/password/email/password-reset-verify', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + email: email, + code: code + }) + }); + if (response?.status === 200) { + return response; + } + + throw new Error( + 'Something went wrong during email verification on password reset.' + ); +}; + +export default EmailVerifyOnPasswordReset; diff --git a/frontend/pages/api/auth/SendEmailOnPasswordReset.ts b/frontend/pages/api/auth/SendEmailOnPasswordReset.ts new file mode 100644 index 000000000..abe610f07 --- /dev/null +++ b/frontend/pages/api/auth/SendEmailOnPasswordReset.ts @@ -0,0 +1,33 @@ +interface Props { + email: string; +} + +/** + * This is the first of the account recovery step (a user needs to verify their email). + * It will send an email containing a magic link to start the account recovery flow. + * @param {object} obj + * @param {object} obj.email - email of a user that is trying to recover access to their account + * @returns + */ +const SendEmailOnPasswordReset = async ({ email }: Props) => { + const response = await fetch('/api/v1/password/email/password-reset', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + email: email + }) + }); + // need precise error handling about the status code + if (response?.status === 200) { + const data = await response.json(); + return data; + } + + throw new Error( + 'Something went wrong while sending the email verification for password reset.' + ); +}; + +export default SendEmailOnPasswordReset; diff --git a/frontend/pages/api/auth/getBackupEncryptedPrivateKey.ts b/frontend/pages/api/auth/getBackupEncryptedPrivateKey.ts new file mode 100644 index 000000000..826085263 --- /dev/null +++ b/frontend/pages/api/auth/getBackupEncryptedPrivateKey.ts @@ -0,0 +1,26 @@ +/** + * This is the route that get an encrypted private key (will be decrypted with a backup key) + * @param {object} obj + * @param {object} obj.verificationToken - this is the token that confirms that a user is the right one + * @returns + */ +const getBackupEncryptedPrivateKey = ({ + verificationToken +}: { + verificationToken: string; +}) => { + return fetch('/api/v1/password/backup-private-key', { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer ' + verificationToken + } + }).then(async (res) => { + if (res?.status !== 200) { + console.log('Failed to get the backup key'); + } + return (await res?.json())?.backupPrivateKey; + }); +}; + +export default getBackupEncryptedPrivateKey; diff --git a/frontend/pages/api/auth/resetPasswordOnAccountRecovery.ts b/frontend/pages/api/auth/resetPasswordOnAccountRecovery.ts new file mode 100644 index 000000000..f2b5ad98e --- /dev/null +++ b/frontend/pages/api/auth/resetPasswordOnAccountRecovery.ts @@ -0,0 +1,50 @@ +interface Props { + verificationToken: string; + encryptedPrivateKey: string; + iv: string; + tag: string; + salt: string; + verifier: string; +} + +/** + * This is the route that resets the account password if all the previus steps were passed + * @param {object} obj + * @param {object} obj.verificationToken - this is the token that confirms that a user is the right one + * @param {object} obj.encryptedPrivateKey - the new encrypted private key (encrypted using the new password) + * @param {object} obj.iv + * @param {object} obj.tag + * @param {object} obj.salt + * @param {object} obj.verifier + * @returns + */ +const resetPasswordOnAccountRecovery = ({ + verificationToken, + encryptedPrivateKey, + iv, + tag, + salt, + verifier +}: Props) => { + return fetch('/api/v1/password/password-reset', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer ' + verificationToken + }, + body: JSON.stringify({ + encryptedPrivateKey: encryptedPrivateKey, + iv: iv, + tag: tag, + salt: salt, + verifier: verifier + }) + }).then(async (res) => { + if (res?.status !== 200) { + console.log('Failed to get the backup key'); + } + return res; + }); +}; + +export default resetPasswordOnAccountRecovery; diff --git a/frontend/pages/email-not-verified.js b/frontend/pages/email-not-verified.js new file mode 100644 index 000000000..11aec70bd --- /dev/null +++ b/frontend/pages/email-not-verified.js @@ -0,0 +1,22 @@ +import React from 'react'; +import Head from 'next/head'; + +export default function Activity() { + return ( +
+ + Request a New Invite + + +
+

Oops.

+

Your email was not verified.

+

Please try again.

+

+ Note: If it still {"doesn't work"}, please reach out to us at + support@infisical.com +

+
+
+ ); +} diff --git a/frontend/pages/login.js b/frontend/pages/login.tsx similarity index 62% rename from frontend/pages/login.js rename to frontend/pages/login.tsx index 2b5d6062c..63dae65a7 100644 --- a/frontend/pages/login.js +++ b/frontend/pages/login.tsx @@ -1,34 +1,37 @@ -import React, { useEffect, useState } from "react"; -import Head from "next/head"; -import Image from "next/image"; -import Link from "next/link"; -import { useRouter } from "next/router"; -import { faWarning } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import React, { useEffect, useState } from 'react'; +import Head from 'next/head'; +import Image from 'next/image'; +import Link from 'next/link'; +import { useRouter } from 'next/router'; +import { faWarning } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import Button from "~/components/basic/buttons/Button"; -import Error from "~/components/basic/Error"; -import InputField from "~/components/basic/InputField"; -import attemptLogin from "~/utilities/attemptLogin"; +import Button from '~/components/basic/buttons/Button'; +import Error from '~/components/basic/Error'; +import InputField from '~/components/basic/InputField'; +import attemptLogin from '~/utilities/attemptLogin'; -import getWorkspaces from "./api/workspace/getWorkspaces"; +import getWorkspaces from './api/workspace/getWorkspaces'; export default function Login() { - const [email, setEmail] = useState(""); - const [password, setPassword] = useState(""); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); const [errorLogin, setErrorLogin] = useState(false); const [isLoading, setIsLoading] = useState(false); const router = useRouter(); - useEffect(async () => { - let userWorkspace; - try { - const userWorkspaces = await getWorkspaces(); - userWorkspace = userWorkspaces[0]._id; - router.push("/dashboard/" + userWorkspace); - } catch (error) { - console.log("Error - Not logged in yet"); - } + useEffect(() => { + const redirectToDashboard = async () => { + let userWorkspace; + try { + const userWorkspaces = await getWorkspaces(); + userWorkspace = userWorkspaces[0]._id; + router.push('/dashboard/' + userWorkspace); + } catch (error) { + console.log('Error - Not logged in yet'); + } + }; + redirectToDashboard(); }, []); /** @@ -73,23 +76,9 @@ export default function Login() {
-

- Log In +

+ Log in to your account

-
-

- Need an Infisical account? -

-
-
- - - -
-
+
+
+ Forgot password? +
{errorLogin && }
@@ -135,6 +127,16 @@ export default function Login() { solving it right now. Please come back in a few minutes.
)} +
+

+ Need an Infisical account? +

+ + + +
); } diff --git a/frontend/pages/password-reset.tsx b/frontend/pages/password-reset.tsx new file mode 100644 index 000000000..a09b85ab0 --- /dev/null +++ b/frontend/pages/password-reset.tsx @@ -0,0 +1,290 @@ +import React, { useState } from 'react'; +import Image from 'next/image'; +import { useRouter } from 'next/router'; +import { faCheck, faX } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; + +import Button from '~/components/basic/buttons/Button'; +import InputField from '~/components/basic/InputField'; +import passwordCheck from '~/components/utilities/checks/PasswordCheck'; +import Aes256Gcm from '~/components/utilities/cryptography/aes-256-gcm'; + +import EmailVerifyOnPasswordReset from './api/auth/EmailVerifyOnPasswordReset'; +import getBackupEncryptedPrivateKey from './api/auth/getBackupEncryptedPrivateKey'; +import resetPasswordOnAccountRecovery from './api/auth/resetPasswordOnAccountRecovery'; + +const queryString = require('query-string'); +const nacl = require('tweetnacl'); +const jsrp = require('jsrp'); +nacl.util = require('tweetnacl-util'); +const client = new jsrp.client(); + +export default function PasswordReset() { + const router = useRouter(); + const parsedUrl = queryString.parse(router.asPath.split('?')[1]); + const token = parsedUrl.token; + const email = parsedUrl.to?.replace(' ', '+').trim(); + const [verificationToken, setVerificationToken] = useState(''); + const [step, setStep] = useState(1); + const [backupKey, setBackupKey] = useState(''); + const [privateKey, setPrivateKey] = useState(''); + const [newPassword, setNewPassword] = useState(''); + const [backupKeyError, setBackupKeyError] = useState(false); + const [passwordErrorLength, setPasswordErrorLength] = useState(false); + const [passwordErrorNumber, setPasswordErrorNumber] = useState(false); + const [passwordErrorLowerCase, setPasswordErrorLowerCase] = useState(false); + + // Unencrypt the private key with a backup key + const getEncryptedKeyHandler = async () => { + try { + const result = await getBackupEncryptedPrivateKey({ verificationToken }); + setPrivateKey( + Aes256Gcm.decrypt({ + ciphertext: result.encryptedPrivateKey, + iv: result.iv, + tag: result.tag, + secret: backupKey + }) + ); + setStep(3); + } catch { + setBackupKeyError(true); + } + }; + + // If everything is correct, reset the password + const resetPasswordHandler = async () => { + let errorCheck = false; + errorCheck = passwordCheck({ + password: newPassword, + setPasswordErrorLength, + setPasswordErrorNumber, + setPasswordErrorLowerCase, + currentErrorCheck: errorCheck + }); + + if (!errorCheck) { + // Generate a random pair of a public and a private key + const { ciphertext, iv, tag } = Aes256Gcm.encrypt({ + text: privateKey, + secret: newPassword + .slice(0, 32) + .padStart( + 32 + + (newPassword.slice(0, 32).length - new Blob([newPassword]).size), + '0' + ) + }) as { ciphertext: string; iv: string; tag: string }; + + client.init( + { + username: email, + password: newPassword + }, + async () => { + client.createVerifier( + async (err: any, result: { salt: string; verifier: string }) => { + const response = await resetPasswordOnAccountRecovery({ + verificationToken, + encryptedPrivateKey: ciphertext, + iv, + tag, + salt: result.salt, + verifier: result.verifier + }); + + // if everything works, go the main dashboard page. + if (response?.status === 200) { + router.push('/login'); + } + } + ); + } + ); + } + }; + + // Click a button to confirm email + const stepConfirmEmail = ( +
+

+ Confirm your email +

+ verify email +
+
+
+ ); + + // Input backup key + const stepInputBackupKey = ( +
+

+ Enter your backup key +

+
+

+ You can find it in your emrgency kit. You had to download the enrgency + kit during signup. +

+
+
+ +
+
+
+
+
+
+ ); + + // Enter new password + const stepEnterNewPassword = ( +
+

+ Enter new password +

+
+

+ Make sure you save it somewhere save. +

+
+
+ { + setNewPassword(password); + passwordCheck({ + password, + setPasswordErrorLength, + setPasswordErrorNumber, + setPasswordErrorLowerCase, + currentErrorCheck: false + }); + }} + type="password" + value={newPassword} + isRequired + error={ + passwordErrorLength && passwordErrorLowerCase && passwordErrorNumber + } + autoComplete="new-password" + id="new-password" + /> +
+ {passwordErrorLength || passwordErrorLowerCase || passwordErrorNumber ? ( +
+
+ Password should contain at least: +
+
+ {passwordErrorLength ? ( + + ) : ( + + )} +
+ 14 characters +
+
+
+ {passwordErrorLowerCase ? ( + + ) : ( + + )} +
+ 1 lowercase character +
+
+
+ {passwordErrorNumber ? ( + + ) : ( + + )} +
+ 1 number +
+
+
+ ) : ( +
+ )} +
+
+
+
+
+ ); + + return ( +
+ {step === 1 && stepConfirmEmail} + {step === 2 && stepInputBackupKey} + {step === 3 && stepEnterNewPassword} +
+ ); +} diff --git a/frontend/pages/signupinvite.js b/frontend/pages/signupinvite.js index 26b6e1544..a41010aea 100644 --- a/frontend/pages/signupinvite.js +++ b/frontend/pages/signupinvite.js @@ -33,7 +33,7 @@ export default function SignupInvite() { const [passwordErrorLowerCase, setPasswordErrorLowerCase] = useState(false); const router = useRouter(); const parsedUrl = queryString.parse(router.asPath.split('?')[1]); - const [email, setEmail] = useState(parsedUrl.to); + const [email, setEmail] = useState(parsedUrl.to?.replace(' ', '+').trim()); const token = parsedUrl.token; const [errorLogin, setErrorLogin] = useState(false); const [isLoading, setIsLoading] = useState(false); @@ -58,13 +58,13 @@ export default function SignupInvite() { } else { setLastNameError(false); } - errorCheck = passwordCheck( + errorCheck = passwordCheck({ password, setPasswordErrorLength, setPasswordErrorNumber, setPasswordErrorLowerCase, errorCheck - ); + }); if (!errorCheck) { // Generate a random pair of a public and a private key @@ -150,7 +150,7 @@ export default function SignupInvite() { width={410} alt="verify email" > -
+
-
+
+
+
+ )} + {step == 2 && ( +
+

+ Look for an email in your inbox. +

+
+

+ An email with instructions has been sent to {email}. +

+
+
+ )} +
+ ); +} diff --git a/frontend/scripts/healthcheck.js b/frontend/scripts/healthcheck.js new file mode 100644 index 000000000..30a964d9d --- /dev/null +++ b/frontend/scripts/healthcheck.js @@ -0,0 +1,23 @@ +const http = require('http'); +const options = { + host: 'localhost', + port: 3000, + timeout: 2000, + path: '/' +}; + +const healthCheck = http.request(options, (res) => { + console.log(`HEALTHCHECK STATUS: ${res.statusCode}`); + if (res.statusCode == 200) { + process.exit(0); + } else { + process.exit(1); + } +}); + +healthCheck.on('error', function (err) { + console.error(`HEALTH CHECK ERROR: ${err}`); + process.exit(1); +}); + +healthCheck.end(); diff --git a/helm-charts/infisical/Chart.yaml b/helm-charts/infisical/Chart.yaml index 48eb5d4dc..a9297b385 100644 --- a/helm-charts/infisical/Chart.yaml +++ b/helm-charts/infisical/Chart.yaml @@ -7,7 +7,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.2 +version: 0.1.3 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/helm-charts/infisical/templates/backend-deployment.yaml b/helm-charts/infisical/templates/backend-deployment.yaml index c2620ff8a..6a2faa688 100644 --- a/helm-charts/infisical/templates/backend-deployment.yaml +++ b/helm-charts/infisical/templates/backend-deployment.yaml @@ -22,11 +22,11 @@ spec: - containerPort: 4000 env: {{- range $key, $value := .Values.backendEnvironmentVariables }} - {{- if eq $value "MUST_REPLACE" }} + {{- if $value | quote | eq "MUST_REPLACE" }} {{ fail "Environment variables are not set. Please set all environment variables to continue." }} {{ end }} - name: {{ $key }} - value: {{ $value }} + value: {{ quote $value }} {{- end }} --- diff --git a/helm-charts/infisical/templates/frontend-deployment.yaml b/helm-charts/infisical/templates/frontend-deployment.yaml index 79bf8837b..3cd29f326 100644 --- a/helm-charts/infisical/templates/frontend-deployment.yaml +++ b/helm-charts/infisical/templates/frontend-deployment.yaml @@ -20,6 +20,9 @@ spec: imagePullPolicy: {{ .Values.frontend.image.pullPolicy }} env: {{- range $key, $value := .Values.frontendEnvironmentVariables }} + {{- if $value | quote | eq "MUST_REPLACE" }} + {{ fail "Environment variables are not set. Please set all environment variables to continue." }} + {{ end }} - name: {{ $key }} value: {{ quote $value }} {{- end }}