From c68eaa613c00170d4ee1fa67e05e14f5ae66383b Mon Sep 17 00:00:00 2001 From: Reginald Bondoc Date: Tue, 29 Nov 2022 21:09:50 +0100 Subject: [PATCH 01/50] Use pre-built frontend image in prod compose file --- docker-compose.yml | 5 +---- frontend/Dockerfile | 49 +++++++++++++++++++++++++++++++++++++++++ frontend/next.config.js | 10 +++------ 3 files changed, 53 insertions(+), 11 deletions(-) create mode 100644 frontend/Dockerfile diff --git a/docker-compose.yml b/docker-compose.yml index 3204f9257..6fd3a951d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -39,9 +39,6 @@ services: restart: unless-stopped depends_on: - backend - build: - context: ./frontend - dockerfile: Dockerfile.prod image: infisical/frontend env_file: .env environment: @@ -73,4 +70,4 @@ volumes: driver: local networks: - infisical: \ No newline at end of file + infisical: diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 000000000..cc65b2eba --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,49 @@ +FROM node:16-alpine AS deps +# Install dependencies only when needed. Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +# RUN apk add --no-cache libc6-compat +WORKDIR /app + +# Copy over dependency files +COPY package.json package-lock.json next.config.js ./ + +# Install dependencies +RUN npm ci --only-production + + +# Rebuild the source code only when needed +FROM node:16-alpine AS builder +WORKDIR /app + +# Copy dependencies +COPY --from=deps /app/node_modules ./node_modules +# Copy all files +COPY . . + +# Build +RUN npm run build + + +# Production image, copy all the files and run next +FROM node:16-alpine AS runner +WORKDIR /app + +ENV NODE_ENV production + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +COPY --from=builder /app/public ./public + +# Automatically leverage output traces to reduce image size +# https://nextjs.org/docs/advanced-features/output-file-tracing +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +USER nextjs + +EXPOSE 3000 + +ENV PORT 3000 + + +CMD ["node", "server.js"] diff --git a/frontend/next.config.js b/frontend/next.config.js index 0711da572..caa9ec566 100644 --- a/frontend/next.config.js +++ b/frontend/next.config.js @@ -1,16 +1,12 @@ // next.config.js const ContentSecurityPolicy = ` - default-src ${process.env.NEXT_PUBLIC_WEBSITE_URL}; - script-src ${ - process.env.NEXT_PUBLIC_WEBSITE_URL - } https://app.posthog.com https://infisical.com https://assets.calendly.com/ https://js.stripe.com https://api.stripe.com 'unsafe-inline' 'unsafe-eval'; + default-src 'self'; + script-src 'self' https://app.posthog.com https://infisical.com https://assets.calendly.com/ https://js.stripe.com https://api.stripe.com 'unsafe-inline' 'unsafe-eval'; style-src 'self' https://rsms.me 'unsafe-inline'; child-src https://infisical.com https://api.stripe.com; frame-src https://js.stripe.com/ https://api.stripe.com; - connect-src ws://${process.env.NEXT_PUBLIC_WEBSITE_URL?.split("//")[1]} ${ - process.env.NEXT_PUBLIC_WEBSITE_URL -} https://api.github.com/repos/Infisical/infisical-cli https://api.heroku.com/ https://id.heroku.com/oauth/authorize https://id.heroku.com/oauth/token https://checkout.stripe.com https://app.posthog.com https://infisical.com https://api.stripe.com https://vitals.vercel-insights.com/v1/vitals; + connect-src 'self' ws: wss: https://api.github.com/repos/Infisical/infisical-cli https://api.heroku.com/ https://id.heroku.com/oauth/authorize https://id.heroku.com/oauth/token https://checkout.stripe.com https://app.posthog.com https://infisical.com https://api.stripe.com https://vitals.vercel-insights.com/v1/vitals; img-src 'self' https://*.stripe.com https://i.ytimg.com/ data:; media-src; font-src 'self' https://maxcdn.bootstrapcdn.com https://rsms.me https://fonts.gstatic.com; From 4ae88b2e4725a9f31a0dc0a714fc8f1440090d77 Mon Sep 17 00:00:00 2001 From: Reginald Bondoc Date: Sat, 3 Dec 2022 17:14:48 +0100 Subject: [PATCH 02/50] Fix merge conflict --- frontend/next.config.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/frontend/next.config.js b/frontend/next.config.js index 1c883c4b9..2ae4b7641 100644 --- a/frontend/next.config.js +++ b/frontend/next.config.js @@ -2,19 +2,11 @@ const ContentSecurityPolicy = ` default-src 'self'; -<<<<<<< HEAD - script-src 'self' https://app.posthog.com https://infisical.com https://assets.calendly.com/ https://js.stripe.com https://api.stripe.com 'unsafe-inline' 'unsafe-eval'; -======= script-src 'self' https://app.posthog.com https://js.stripe.com https://api.stripe.com 'unsafe-inline' 'unsafe-eval'; ->>>>>>> main style-src 'self' https://rsms.me 'unsafe-inline'; child-src https://api.stripe.com; frame-src https://js.stripe.com/ https://api.stripe.com; -<<<<<<< HEAD - connect-src 'self' ws: wss: https://api.github.com/repos/Infisical/infisical-cli https://api.heroku.com/ https://id.heroku.com/oauth/authorize https://id.heroku.com/oauth/token https://checkout.stripe.com https://app.posthog.com https://infisical.com https://api.stripe.com https://vitals.vercel-insights.com/v1/vitals; -======= connect-src 'self' https://api.heroku.com/ https://id.heroku.com/oauth/authorize https://id.heroku.com/oauth/token https://checkout.stripe.com https://app.posthog.com https://api.stripe.com; ->>>>>>> main img-src 'self' https://*.stripe.com https://i.ytimg.com/ data:; media-src; font-src 'self' https://maxcdn.bootstrapcdn.com https://rsms.me https://fonts.gstatic.com; From b677ab642960a5ba08aba996613eb28785b46ad6 Mon Sep 17 00:00:00 2001 From: asharonbaltazar Date: Sun, 4 Dec 2022 22:03:07 -0500 Subject: [PATCH 03/50] feat: add react TS types --- frontend/package-lock.json | 107 ++++++++++--------------------------- frontend/package.json | 1 + 2 files changed, 28 insertions(+), 80 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7a560ad3f..7a8ed6702 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -50,13 +50,13 @@ "devDependencies": { "@tailwindcss/typography": "^0.5.4", "@types/node": "18.11.9", + "@types/react": "^18.0.26", "@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/parser": "^5.45.0", "autoprefixer": "^10.4.7", "eslint": "^8.29.0", "eslint-config-next": "^13.0.5", "eslint-import-resolver-typescript": "^3.5.2", - "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-simple-import-sort": "^8.0.0", "postcss": "^8.4.14", "prettier": "2.7.1", @@ -1143,9 +1143,9 @@ "optional": true }, "node_modules/@types/react": { - "version": "18.0.15", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.15.tgz", - "integrity": "sha512-iz3BtLuIYH1uWdsv6wXYdhozhqj20oD4/Hk2DNXIn1kFsmp9x8d9QB6FnPhfkbhd2PgEONt9Q1x/ebkwjfFLow==", + "version": "18.0.26", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", + "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -2901,27 +2901,6 @@ "semver": "bin/semver.js" } }, - "node_modules/eslint-plugin-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", - "dev": true, - "dependencies": { - "prettier-linter-helpers": "^1.0.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "eslint": ">=7.28.0", - "prettier": ">=2.0.0" - }, - "peerDependenciesMeta": { - "eslint-config-prettier": { - "optional": true - } - } - }, "node_modules/eslint-plugin-react": { "version": "7.31.11", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.11.tgz", @@ -3289,12 +3268,6 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "node_modules/fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", - "dev": true - }, "node_modules/fast-glob": { "version": "3.2.11", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", @@ -5624,18 +5597,6 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -7666,7 +7627,8 @@ "@headlessui/react": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.6.6.tgz", - "integrity": "sha512-MFJtmj9Xh/hhBMhLccGbBoSk+sk61BlP6sJe4uQcVMtXZhCgGqd2GyIQzzmsdPdTEWGSF434CBi8mnhR6um46Q==" + "integrity": "sha512-MFJtmj9Xh/hhBMhLccGbBoSk+sk61BlP6sJe4uQcVMtXZhCgGqd2GyIQzzmsdPdTEWGSF434CBi8mnhR6um46Q==", + "requires": {} }, "@humanwhocodes/config-array": { "version": "0.11.7", @@ -8007,9 +7969,9 @@ "optional": true }, "@types/react": { - "version": "18.0.15", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.15.tgz", - "integrity": "sha512-iz3BtLuIYH1uWdsv6wXYdhozhqj20oD4/Hk2DNXIn1kFsmp9x8d9QB6FnPhfkbhd2PgEONt9Q1x/ebkwjfFLow==", + "version": "18.0.26", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", + "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", "requires": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -8247,7 +8209,8 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true + "dev": true, + "requires": {} }, "acorn-node": { "version": "1.8.2", @@ -8450,7 +8413,8 @@ "axios-auth-refresh": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/axios-auth-refresh/-/axios-auth-refresh-3.3.3.tgz", - "integrity": "sha512-2IbDhJ/h6ddNBBnnzn1VFK/qx17pE9aVqiafB8rx5LVHsJ1HtFpUGkbXY7PzTG+8P9HJWcyA3fNZl9BikSuilg==" + "integrity": "sha512-2IbDhJ/h6ddNBBnnzn1VFK/qx17pE9aVqiafB8rx5LVHsJ1HtFpUGkbXY7PzTG+8P9HJWcyA3fNZl9BikSuilg==", + "requires": {} }, "axobject-query": { "version": "2.2.0", @@ -9400,15 +9364,6 @@ } } }, - "eslint-plugin-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", - "dev": true, - "requires": { - "prettier-linter-helpers": "^1.0.0" - } - }, "eslint-plugin-react": { "version": "7.31.11", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.11.tgz", @@ -9464,13 +9419,15 @@ "version": "4.6.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", - "dev": true + "dev": true, + "requires": {} }, "eslint-plugin-simple-import-sort": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-8.0.0.tgz", "integrity": "sha512-bXgJQ+lqhtQBCuWY/FUWdB27j4+lqcvXv5rUARkzbeWLwea+S5eBZEQrhnO+WgX3ZoJHVj0cn943iyXwByHHQw==", - "dev": true + "dev": true, + "requires": {} }, "eslint-scope": { "version": "7.1.1", @@ -9583,12 +9540,6 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", - "dev": true - }, "fast-glob": { "version": "3.2.11", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", @@ -11161,15 +11112,6 @@ "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", "dev": true }, - "prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "requires": { - "fast-diff": "^1.1.2" - } - }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -11441,7 +11383,8 @@ "react-table": { "version": "7.8.0", "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz", - "integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==" + "integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==", + "requires": {} }, "read-cache": { "version": "1.0.0", @@ -11482,7 +11425,8 @@ "redux-thunk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz", - "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==" + "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==", + "requires": {} }, "regenerator-runtime": { "version": "0.13.11", @@ -11834,7 +11778,8 @@ "styled-jsx": { "version": "5.0.7", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.7.tgz", - "integrity": "sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==" + "integrity": "sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==", + "requires": {} }, "stylis": { "version": "4.0.13", @@ -12197,12 +12142,14 @@ "use-memo-one": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", - "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==" + "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==", + "requires": {} }, "use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", - "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==" + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "requires": {} }, "util-deprecate": { "version": "1.0.2", diff --git a/frontend/package.json b/frontend/package.json index ebfdbfe3c..ab71c81bf 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -53,6 +53,7 @@ "devDependencies": { "@tailwindcss/typography": "^0.5.4", "@types/node": "18.11.9", + "@types/react": "^18.0.26", "@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/parser": "^5.45.0", "autoprefixer": "^10.4.7", From bb47f7a92f134ad2774ef18a0a5abb629a7fe27a Mon Sep 17 00:00:00 2001 From: Reginald Bondoc Date: Mon, 5 Dec 2022 21:17:59 +0100 Subject: [PATCH 04/50] Pre-bake some vars and change telemetry handling --- docker-compose.dev.yml | 10 ++--- docker-compose.yml | 10 +---- docs/self-hosting/configuration/envars.mdx | 24 +++++------ frontend/Dockerfile | 29 ++++++++++--- frontend/components/analytics/posthog.js | 3 +- frontend/components/utilities/attemptLogin.js | 18 +++----- frontend/components/utilities/config/index.ts | 3 -- .../utilities/telemetry/Telemetry.js | 43 +++++++++++++++++++ frontend/pages/_app.js | 10 ++--- frontend/scripts/replace-variable.sh | 16 +++++++ frontend/scripts/set-telemetry.sh | 8 ++++ frontend/scripts/start.sh | 14 ++++++ 12 files changed, 130 insertions(+), 58 deletions(-) create mode 100644 frontend/components/utilities/telemetry/Telemetry.js create mode 100644 frontend/scripts/replace-variable.sh create mode 100644 frontend/scripts/set-telemetry.sh create mode 100644 frontend/scripts/start.sh diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 6eabfe4d9..949a96c7d 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -23,7 +23,6 @@ services: build: context: ./backend dockerfile: Dockerfile - image: infisical/backend volumes: - ./backend/src:/app/src - ./backend/nodemon.json:/app/nodemon.json @@ -43,7 +42,6 @@ services: build: context: ./frontend dockerfile: Dockerfile.dev - image: infisical/frontend volumes: - ./frontend/pages:/app/pages - ./frontend/public:/app/public @@ -52,12 +50,8 @@ services: env_file: .env environment: - NEXT_PUBLIC_ENV=development - - NEXT_PUBLIC_WEBSITE_URL=${SITE_URL} - - NEXT_PUBLIC_POSTHOG_HOST=${POSTHOG_HOST} - - NEXT_PUBLIC_POSTHOG_API_KEY=${POSTHOG_PROJECT_API_KEY} - NEXT_PUBLIC_STRIPE_PRODUCT_PRO=${STRIPE_PRODUCT_PRO} - NEXT_PUBLIC_STRIPE_PRODUCT_STARTER=${STRIPE_PRODUCT_STARTER} - - NEXT_PUBLIC_TELEMETRY_ENABLED=${TELEMETRY_ENABLED} networks: - infisical-dev @@ -78,6 +72,8 @@ services: container_name: infisical-dev-mongo-express image: mongo-express restart: always + depends_on: + - mongo env_file: .env environment: - ME_CONFIG_MONGODB_ADMINUSERNAME=${MONGO_USERNAME} @@ -93,4 +89,4 @@ volumes: driver: local networks: - infisical-dev: \ No newline at end of file + infisical-dev: diff --git a/docker-compose.yml b/docker-compose.yml index 6fd3a951d..c719464ae 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,9 +22,6 @@ services: restart: unless-stopped depends_on: - mongo - build: - context: ./backend - dockerfile: Dockerfile image: infisical/backend command: npm run start env_file: .env @@ -42,13 +39,10 @@ services: image: infisical/frontend env_file: .env environment: - - NEXT_PUBLIC_ENV=production - - NEXT_PUBLIC_WEBSITE_URL=${SITE_URL} - - NEXT_PUBLIC_POSTHOG_HOST=${POSTHOG_HOST} - - NEXT_PUBLIC_POSTHOG_API_KEY=${POSTHOG_PROJECT_API_KEY} + # - NEXT_PUBLIC_POSTHOG_API_KEY=${POSTHOG_PROJECT_API_KEY} + - INFISICAL_TELEMETRY_ENABLED=${TELEMETRY_ENABLED} - NEXT_PUBLIC_STRIPE_PRODUCT_PRO=${STRIPE_PRODUCT_PRO} - NEXT_PUBLIC_STRIPE_PRODUCT_STARTER=${STRIPE_PRODUCT_STARTER} - - NEXT_PUBLIC_TELEMETRY_ENABLED=${TELEMETRY_ENABLED} networks: - infisical diff --git a/docs/self-hosting/configuration/envars.mdx b/docs/self-hosting/configuration/envars.mdx index bea36da32..a55efbebc 100644 --- a/docs/self-hosting/configuration/envars.mdx +++ b/docs/self-hosting/configuration/envars.mdx @@ -9,24 +9,24 @@ Configuring Infisical requires setting some environment variables. There is a fi | Variable | Description | Default Value | | ---------------------------- | ----------------------------------------------------------------------------------------------------------- | ---------------- | -| `PRIVATE_KEY` | ❗️ NaCl-generated server secret key | `None` | -| `PUBLIC_KEY` | ❗️ NaCl-generated server public key | `None` | -| `ENCRYPTION_KEY` | ❗️ Strong hex encryption key | `None` | -| `JWT_SIGNUP_SECRET` | ❗️JWT token secret | `None` | -| `JWT_REFRESH_SECRET` | ❗️ JWT token secret | `None` | -| `JWT_AUTH_SECRET` | ❗️ JWT token secret | `None` | +| `PRIVATE_KEY` | ❗️ NaCl-generated server secret key | `None` | +| `PUBLIC_KEY` | ❗️ NaCl-generated server public key | `None` | +| `ENCRYPTION_KEY` | ❗️ Strong hex encryption key | `None` | +| `JWT_SIGNUP_SECRET` | ❗️ JWT token secret | `None` | +| `JWT_REFRESH_SECRET` | ❗️ JWT token secret | `None` | +| `JWT_AUTH_SECRET` | ❗️ JWT token secret | `None` | | `JWT_SIGNUP_LIFETIME` | JWT token lifetime expressed in seconds or a string describing a time span (e.g. 60, "2 days", "10h", "7d") | `15m` | | `JWT_REFRESH_LIFETIME` | JWT token lifetime expressed in seconds or a string describing a time span (e.g. 60, "2 days", "10h", "7d") | `90d` | | `JWT_AUTH_LIFETIME` | JWT token lifetime expressed in seconds or a string describing a time span (e.g. 60, "2 days", "10h", "7d") | `10d` | | `EMAIL_TOKEN_LIFETIME` | Email OTP/magic-link lifetime expressed in seconds | `86400` | -| `MONGO_URL` | ❗️ MongoDB instance connection string either to container instance or MongoDB Cloud | `None` | +| `MONGO_URL` | ❗️ MongoDB instance connection string either to container instance or MongoDB Cloud | `None` | | `MONGO_USERNAME` | MongoDB username if using container | `None` | | `MONGO_PASSWORD` | MongoDB password if using container | `None` | -| `SITE_URL` | ❗️ Site URL - should be an absolute URL including the protocol (e.g. `https://app.infisical.com`) | `None` | -| `SMT_HOST` | Whether the user joined the community | `smtp.gmail.com` | -| `SMTP_NAME` | Hostname to connect to for establishing SMTP connections (e.g. `Team`) | `None` | -| `SMTP_USERNAME` | ❗️ Credential to connect to host (e.g. `team@infisical.com`) | `None` | -| `SMTP_PASSWORD` | ❗️ Credential to connect to host | `None` | +| `SITE_URL` | ❗️ Site URL - should be an absolute URL including the protocol (e.g. `https://app.infisical.com`) | `None` | +| `SMTP_HOST` | Hostname to connect to for establishing SMTP connections | `smtp.gmail.com` | +| `SMTP_NAME` | Name label to be used in From field (e.g. `Team`) | `None` | +| `SMTP_USERNAME` | ❗️ Credential to connect to host (e.g. `team@infisical.com`) | `None` | +| `SMTP_PASSWORD` | ❗️ Credential to connect to host | `None` | | `TELEMETRY_ENABLED` | `true` or `false`. [More](../overview). | `true` | | `OAUTH_CLIENT_SECRET_HEROKU` | OAuth client secret for Heroku integration | `None` | | `OAUTH_TOKEN_URL_HEROKU` | OAuth token URL for Heroku integration | `None` | diff --git a/frontend/Dockerfile b/frontend/Dockerfile index cc65b2eba..5e59c68aa 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,3 +1,6 @@ +ARG POSTHOG_HOST=https://app.posthog.com +ARG POSTHOG_API_KEY=posthog-api-key + FROM node:16-alpine AS deps # Install dependencies only when needed. Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. # RUN apk add --no-cache libc6-compat @@ -19,23 +22,34 @@ COPY --from=deps /app/node_modules ./node_modules # Copy all files COPY . . +ENV NODE_ENV production +ENV NEXT_PUBLIC_ENV production +ARG POSTHOG_HOST +ENV NEXT_PUBLIC_POSTHOG_HOST $POSTHOG_HOST +ARG POSTHOG_API_KEY +ENV NEXT_PUBLIC_POSTHOG_API_KEY $POSTHOG_API_KEY + # Build RUN npm run build -# Production image, copy all the files and run next +# Production image FROM node:16-alpine AS runner WORKDIR /app -ENV NODE_ENV production - RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs -COPY --from=builder /app/public ./public +RUN mkdir -p /app/.next/cache/images && chown nextjs:nodejs /app/.next/cache/images +VOLUME /app/.next/cache/images -# Automatically leverage output traces to reduce image size -# https://nextjs.org/docs/advanced-features/output-file-tracing +ARG POSTHOG_API_KEY +ENV NEXT_PUBLIC_POSTHOG_API_KEY=$POSTHOG_API_KEY \ + BAKED_NEXT_PUBLIC_POSTHOG_API_KEY=$POSTHOG_API_KEY + +COPY --chown=nextjs:nodejs --chmod=555 scripts ./scripts +COPY --from=builder /app/public ./public +RUN chown nextjs:nodejs ./public/data COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static @@ -44,6 +58,7 @@ USER nextjs EXPOSE 3000 ENV PORT 3000 +ENV NEXT_TELEMETRY_DISABLED 1 -CMD ["node", "server.js"] +CMD ["/app/scripts/start.sh"] diff --git a/frontend/components/analytics/posthog.js b/frontend/components/analytics/posthog.js index c8d51ad1b..be12ba418 100644 --- a/frontend/components/analytics/posthog.js +++ b/frontend/components/analytics/posthog.js @@ -4,12 +4,11 @@ import { ENV, POSTHOG_API_KEY, POSTHOG_HOST, - TELEMETRY_ENABLED, } from "../utilities/config"; export const initPostHog = () => { if (typeof window !== "undefined") { - if (ENV == "production" && TELEMETRY_ENABLED) { + if (ENV == "production" && TELEMETRY_CAPTURING_ENABLED) { posthog.init(POSTHOG_API_KEY, { api_host: POSTHOG_HOST, }); diff --git a/frontend/components/utilities/attemptLogin.js b/frontend/components/utilities/attemptLogin.js index 767fcb400..5fe00dc9a 100644 --- a/frontend/components/utilities/attemptLogin.js +++ b/frontend/components/utilities/attemptLogin.js @@ -4,10 +4,9 @@ import login2 from "~/pages/api/auth/Login2"; import getOrganizations from "~/pages/api/organization/getOrgs"; import getOrganizationUserProjects from "~/pages/api/organization/GetOrgUserProjects"; -import { initPostHog } from "../analytics/posthog"; import pushKeys from "./secrets/pushKeys"; -import { ENV } from "./config"; import SecurityClient from "./SecurityClient"; +import Telemetry from "./telemetry/Telemetry"; const nacl = require("tweetnacl"); nacl.util = require("tweetnacl-util"); @@ -33,6 +32,8 @@ const attemptLogin = async ( ) => { try { let userWorkspace, userOrg; + const telemetry = new Telemetry().getInstance(); + client.init( { username: email, @@ -159,16 +160,9 @@ const attemptLogin = async ( "Development" ); } - try { - if (email) { - if (ENV == "production") { - const posthog = initPostHog(); - posthog.identify(email); - posthog.capture("User Logged In"); - } - } - } catch (error) { - console.log("posthog", error); + if (email) { + telemetry.identify(email); + telemetry.capture("User Logged In"); } if (isLogin) { diff --git a/frontend/components/utilities/config/index.ts b/frontend/components/utilities/config/index.ts index 62549725c..d0ffed00c 100644 --- a/frontend/components/utilities/config/index.ts +++ b/frontend/components/utilities/config/index.ts @@ -4,8 +4,6 @@ const POSTHOG_HOST = process.env.NEXT_PUBLIC_POSTHOG_HOST! || "https://app.posthog.com"; const STRIPE_PRODUCT_PRO = process.env.NEXT_PUBLIC_STRIPE_PRODUCT_PRO!; const STRIPE_PRODUCT_STARTER = process.env.NEXT_PUBLIC_STRIPE_PRODUCT_STARTER!; -const TELEMETRY_ENABLED = - process.env.NEXT_PUBLIC_TELEMETRY_ENABLED! !== "false"; export { ENV, @@ -13,5 +11,4 @@ export { POSTHOG_HOST, STRIPE_PRODUCT_PRO, STRIPE_PRODUCT_STARTER, - TELEMETRY_ENABLED, }; diff --git a/frontend/components/utilities/telemetry/Telemetry.js b/frontend/components/utilities/telemetry/Telemetry.js new file mode 100644 index 000000000..4de6ab5ce --- /dev/null +++ b/frontend/components/utilities/telemetry/Telemetry.js @@ -0,0 +1,43 @@ +import { initPostHog } from "~/components/analytics/posthog"; +import { ENV } from "~/components/utilities/config"; + +class Capturer { + constructor() { + this.api = initPostHog(); + } + + capture(item) { + if (ENV == "production" && TELEMETRY_CAPTURING_ENABLED) { + try { + api.capture(item); + } catch (error) { + console.error("PostHog", error); + } + } + } + + identify(id) { + if (ENV == "production" && TELEMETRY_CAPTURING_ENABLED) { + try { + api.identify(id); + } catch (error) { + console.error("PostHog", error); + } + } + } + +} + +class Telemetry { + constructor() { + if (!Telemetry.instance) { + Telemetry.instance = new Capturer(); + } + } + + getInstance() { + return Telemetry.instance; + } +} + +module.exports = Telemetry; diff --git a/frontend/pages/_app.js b/frontend/pages/_app.js index 6ed9af013..8bc612b3c 100644 --- a/frontend/pages/_app.js +++ b/frontend/pages/_app.js @@ -2,11 +2,10 @@ import { useEffect } from "react"; import { useRouter } from "next/router"; import { config } from "@fortawesome/fontawesome-svg-core"; -import { initPostHog } from "~/components/analytics/posthog"; import Layout from "~/components/basic/layout"; import RouteGuard from "~/components/RouteGuard"; import { publicPaths } from "~/const"; -import { ENV } from "~/utilities/config"; +import Telemetry from "~/utilities/telemetry/Telemetry"; import "@fortawesome/fontawesome-svg-core/styles.css"; import "../styles/globals.css"; @@ -15,17 +14,14 @@ config.autoAddCss = false; const App = ({ Component, pageProps, ...appProps }) => { const router = useRouter(); - const posthog = initPostHog(); useEffect(() => { // Init for auto capturing - const posthog = initPostHog(); + const telemetry = new Telemetry().getInstance(); const handleRouteChange = () => { if (typeof window !== "undefined") { - if (ENV == "production") { - posthog.capture("$pageview"); - } + telemetry.capture("$pageview"); } }; diff --git a/frontend/scripts/replace-variable.sh b/frontend/scripts/replace-variable.sh new file mode 100644 index 000000000..ae449b0e8 --- /dev/null +++ b/frontend/scripts/replace-variable.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +ORIGINAL=$1 +REPLACEMENT=$2 + +if [ "${ORIGINAL}" = "${REPLACEMENT}" ]; then + echo "Replacement is the same, skipping.." + exit 0 +fi + +echo "Replacing pre-baked value.." + +find /app/public /app/.next -type f ! -name "*.png" ! -name "*.svg" ! -name "*.gif" ! -name "*.jpg" ! -name "*.jpeg" ! -name "*.ico" | +while read file; do + sed -i "s|$ORIGINAL|$REPLACEMENT|g" "$file" +done diff --git a/frontend/scripts/set-telemetry.sh b/frontend/scripts/set-telemetry.sh new file mode 100644 index 000000000..75cac7ce3 --- /dev/null +++ b/frontend/scripts/set-telemetry.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +VALUE=$1 + +find /app/public /app/.next -type f ! -name "*.png" ! -name "*.svg" ! -name "*.gif" ! -name "*.jpg" ! -name "*.jpeg" ! -name "*.ico" | +while read file; do + sed -i "s|TELEMETRY_CAPTURING_ENABLED|$VALUE|g" "$file" +done diff --git a/frontend/scripts/start.sh b/frontend/scripts/start.sh new file mode 100644 index 000000000..05a1f219a --- /dev/null +++ b/frontend/scripts/start.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +scripts/replace-variable.sh "$BAKED_NEXT_PUBLIC_POSTHOG_API_KEY" "$NEXT_PUBLIC_POSTHOG_API_KEY" + +if [ "$INFISICAL_TELEMETRY_ENABLED" != "false" ]; then + echo "Telemetry is enabled" + scripts/set-telemetry.sh true +else + echo "Client opted out of telemetry" + scripts/set-telemetry.sh false +fi + + +node server.js From a47decd31f0a4d2a5a2acb2fbd1276cb584f6e20 Mon Sep 17 00:00:00 2001 From: seunghyunOh Date: Tue, 6 Dec 2022 05:47:02 +0900 Subject: [PATCH 05/50] Convert JS to TS --- .../basic/{InputField.js => InputField.tsx} | 32 +- .../basic/{layout.js => Layout.tsx} | 282 ++++++++++-------- frontend/components/basic/Listbox.tsx | 21 +- frontend/components/basic/buttons/Button.tsx | 2 +- .../components/utilities/SecurityClient.js | 2 +- frontend/components/utilities/attemptLogin.js | 11 +- .../{PasswordCheck.js => PasswordCheck.ts} | 19 +- .../utilities/checks/tempLocalStorage.ts | 11 + frontend/pages/_app.js | 2 +- .../api/organization/GetOrgUserProjects.js | 2 +- frontend/pages/api/workspace/getWorkspaces.ts | 9 +- frontend/pages/{signup.js => signup.tsx} | 125 ++++---- frontend/types/workspaces.d.ts | 8 + 13 files changed, 304 insertions(+), 222 deletions(-) rename frontend/components/basic/{InputField.js => InputField.tsx} (89%) rename frontend/components/basic/{layout.js => Layout.tsx} (55%) rename frontend/components/utilities/checks/{PasswordCheck.js => PasswordCheck.ts} (83%) create mode 100644 frontend/components/utilities/checks/tempLocalStorage.ts rename frontend/pages/{signup.js => signup.tsx} (88%) create mode 100644 frontend/types/workspaces.d.ts diff --git a/frontend/components/basic/InputField.js b/frontend/components/basic/InputField.tsx similarity index 89% rename from frontend/components/basic/InputField.js rename to frontend/components/basic/InputField.tsx index 8368d9797..02bb8a8cc 100644 --- a/frontend/components/basic/InputField.js +++ b/frontend/components/basic/InputField.tsx @@ -1,19 +1,27 @@ -import React from "react"; -import { useState } from "react"; +import React, { useState } from "react"; import { useRouter } from "next/router"; -import { - faCircle, - faCircleExclamation, - faE, - faEye, - faEyeSlash, -} from "@fortawesome/free-solid-svg-icons"; +import { faCircle, faEye, faEyeSlash } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import guidGenerator from "../utilities/randomId"; -import Error from "./Error"; -const InputField = (props) => { +interface InputFieldProps { + static?: boolean; + label: string; + type: string; + value: string; + placeholder?: string; + isRequired: boolean; + disabled?: boolean; + error?: boolean; + text?: string; + name?: string; + blurred?: boolean; + errorText?: string; + onChangeHandler: (value: string) => void; +} + +const InputField = (props: InputFieldProps) => { const [passwordVisible, setPasswordVisible] = useState(false); const router = useRouter(); @@ -67,7 +75,7 @@ const InputField = (props) => { > props.onChangeHandler(e.target.value)} - type={passwordVisible == false ? props.type : "text"} + type={passwordVisible === false ? props.type : "text"} placeholder={props.placeholder} value={props.value} required={props.isRequired} diff --git a/frontend/components/basic/layout.js b/frontend/components/basic/Layout.tsx similarity index 55% rename from frontend/components/basic/layout.js rename to frontend/components/basic/Layout.tsx index effe38942..4a40fdef0 100644 --- a/frontend/components/basic/layout.js +++ b/frontend/components/basic/Layout.tsx @@ -1,3 +1,4 @@ +/* eslint-disable no-unexpected-multiline */ /* eslint-disable react-hooks/exhaustive-deps */ import { useEffect, useState } from "react"; import Link from "next/link"; @@ -22,6 +23,7 @@ import getWorkspaces from "~/pages/api/workspace/getWorkspaces"; import uploadKeys from "~/pages/api/workspace/uploadKeys"; import NavBarDashboard from "../navigation/NavBarDashboard"; +import { tempLocalStorage } from "../utilities/checks/tempLocalStorage"; import { decryptAssymmetric, encryptAssymmetric, @@ -30,13 +32,17 @@ import Button from "./buttons/Button"; import AddWorkspaceDialog from "./dialog/AddWorkspaceDialog"; import Listbox from "./Listbox"; -export default function Layout({ children }) { +interface LayoutProps { + children: React.ReactNode; +} + +export default function Layout({ children }: LayoutProps) { const router = useRouter(); const [workspaceList, setWorkspaceList] = useState([]); const [workspaceMapping, setWorkspaceMapping] = useState([{ 1: 2 }]); const [workspaceSelected, setWorkspaceSelected] = useState("∞"); - let [newWorkspaceName, setNewWorkspaceName] = useState(""); - let [isOpen, setIsOpen] = useState(false); + const [newWorkspaceName, setNewWorkspaceName] = useState(""); + const [isOpen, setIsOpen] = useState(false); const [loading, setLoading] = useState(false); const [error, setError] = useState(false); @@ -44,158 +50,186 @@ export default function Layout({ children }) { setIsOpen(false); } + function openModal() { + setIsOpen(true); + } + // TODO: what to do about the fact that 2ids can have the same name /** * When a user creates a new workspace, redirect them to the page of the new workspace. * @param {*} workspaceName */ - async function submitModal(workspaceName, addAllUsers) { + async function submitModal(workspaceName: string, addAllUsers: boolean) { setLoading(true); + // timeout code. setTimeout(() => setLoading(false), 1500); - const workspaces = await getWorkspaces(); - const currentWorkspaces = workspaces.map((workspace) => workspace.name); - if (!currentWorkspaces.includes(workspaceName)) { - const newWorkspace = await createWorkspace({ - workspaceName, - organizationId: localStorage.getItem("orgData.id") - }); - let newWorkspaceId; - try { - newWorkspaceId = newWorkspace._id; - } catch (error) { - console.log(error); - } - if (addAllUsers) { - let orgUsers = await getOrganizationUsers({ - orgId: localStorage.getItem("orgData.id"), + + try { + const workspaces = await getWorkspaces(); + const currentWorkspaces = workspaces.map((workspace) => workspace.name); + if (!currentWorkspaces.includes(workspaceName)) { + const newWorkspace = await createWorkspace({ + workspaceName, + organizationId: tempLocalStorage("orgData.id"), }); - orgUsers.map(async (user) => { - if (user.status == "accepted") { - let result = await addUserToWorkspace( - user.user.email, - newWorkspaceId - ); - if (result?.invitee && result?.latestKey) { - const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY"); + const newWorkspaceId = newWorkspace._id; - // assymmetrically decrypt symmetric key with local private key - const key = decryptAssymmetric({ - ciphertext: result.latestKey.encryptedKey, - nonce: result.latestKey.nonce, - publicKey: result.latestKey.sender.publicKey, - privateKey: PRIVATE_KEY, - }); + if (addAllUsers) { + const orgUsers = await getOrganizationUsers({ + orgId: tempLocalStorage("orgData.id"), + }); + orgUsers.map(async (user: any) => { + if (user.status == "accepted") { + const result = await addUserToWorkspace( + user.user.email, + newWorkspaceId + ); + if (result?.invitee && result?.latestKey) { + const PRIVATE_KEY = tempLocalStorage("PRIVATE_KEY"); - const { ciphertext, nonce } = encryptAssymmetric({ - plaintext: key, - publicKey: result.invitee.publicKey, - privateKey: PRIVATE_KEY, - }); + // assymmetrically decrypt symmetric key with local private key + const key = decryptAssymmetric({ + ciphertext: result.latestKey.encryptedKey, + nonce: result.latestKey.nonce, + publicKey: result.latestKey.sender.publicKey, + privateKey: PRIVATE_KEY, + }); - uploadKeys(newWorkspaceId, result.invitee._id, ciphertext, nonce); + const { ciphertext, nonce } = encryptAssymmetric({ + plaintext: key, + publicKey: result.invitee.publicKey, + privateKey: PRIVATE_KEY, + }) as { ciphertext: string; nonce: string }; + + uploadKeys( + newWorkspaceId, + result.invitee._id, + ciphertext, + nonce + ); + } } - } - }); + }); + } + router.push("/dashboard/" + newWorkspaceId + "?Development"); + setIsOpen(false); + setNewWorkspaceName(""); + } else { + console.error("A project with this name already exists."); + setError(true); + setLoading(false); } - router.push("/dashboard/" + newWorkspaceId + "?Development"); - setIsOpen(false); - setNewWorkspaceName(""); - } else { - setError("A project with this name already exists."); + } catch (err) { + console.error(err); + setError(true); setLoading(false); } } - function openModal() { - setIsOpen(true); - } - const menuItems = [ { href: - "/dashboard/" + workspaceMapping[workspaceSelected] + "?Development", + "/dashboard/" + + workspaceMapping[workspaceSelected as any] + + "?Development", title: "Secrets", emoji: , }, { - href: "/users/" + workspaceMapping[workspaceSelected], + href: "/users/" + workspaceMapping[workspaceSelected as any], title: "Members", emoji: , }, { - href: "/integrations/" + workspaceMapping[workspaceSelected], + href: "/integrations/" + workspaceMapping[workspaceSelected as any], title: "Integrations", emoji: , }, { - href: "/settings/project/" + workspaceMapping[workspaceSelected], + href: "/settings/project/" + workspaceMapping[workspaceSelected as any], title: "Project Settings", emoji: , }, ]; - useEffect(async () => { + useEffect(() => { // Put a user in a workspace if they're not in one yet - if ( - localStorage.getItem("orgData.id") == null || - localStorage.getItem("orgData.id") == "" - ) { - const userOrgs = await getOrganizations(); - localStorage.setItem("orgData.id", userOrgs[0]._id); - } - - let orgUserProjects = await getOrganizationUserProjects({ - orgId: localStorage.getItem("orgData.id"), - }); - let userWorkspaces = orgUserProjects; - if ( - userWorkspaces.length == 0 && - router.asPath != "/noprojects" && - !router.asPath.includes("settings") - ) { - router.push("/noprojects"); - } else if (router.asPath != "/noprojects") { - const intendedWorkspaceId = router.asPath - .split("/")[router.asPath.split("/").length - 1].split("?")[0]; - // If a user is not a member of a workspace they are trying to access, just push them to one of theirs - if ( - intendedWorkspaceId != "heroku" && - !userWorkspaces - .map((workspace) => workspace._id) - .includes(intendedWorkspaceId) - ) { - router.push("/dashboard/" + userWorkspaces[0]._id + "?Development"); - } else { - setWorkspaceList(userWorkspaces.map((workspace) => workspace.name)); - setWorkspaceMapping( - Object.fromEntries( - userWorkspaces.map((workspace) => [workspace.name, workspace._id]) - ) - ); - setWorkspaceSelected( - Object.fromEntries( - userWorkspaces.map((workspace) => [workspace._id, workspace.name]) - )[ - router.asPath - .split("/")[router.asPath.split("/").length - 1].split("?")[0]] - ); + const putUserInWorkSpace = async () => { + if (tempLocalStorage("orgData.id") === "") { + const userOrgs = await getOrganizations(); + localStorage.setItem("orgData.id", userOrgs[0]._id); } - } + + const orgUserProjects = await getOrganizationUserProjects({ + orgId: tempLocalStorage("orgData.id"), + }); + const userWorkspaces = orgUserProjects; + if ( + userWorkspaces.length == 0 && + router.asPath != "/noprojects" && + !router.asPath.includes("settings") + ) { + router.push("/noprojects"); + } else if (router.asPath != "/noprojects") { + const intendedWorkspaceId = router.asPath + .split("/") + [router.asPath.split("/").length - 1].split("?")[0]; + // If a user is not a member of a workspace they are trying to access, just push them to one of theirs + if ( + intendedWorkspaceId != "heroku" && + !userWorkspaces + .map((workspace: { _id: string }) => workspace._id) + .includes(intendedWorkspaceId) + ) { + router.push("/dashboard/" + userWorkspaces[0]._id + "?Development"); + } else { + setWorkspaceList( + userWorkspaces.map((workspace: any) => workspace.name) + ); + setWorkspaceMapping( + Object.fromEntries( + userWorkspaces.map((workspace: any) => [ + workspace.name, + workspace._id, + ]) + ) as any + ); + setWorkspaceSelected( + Object.fromEntries( + userWorkspaces.map((workspace: any) => [ + workspace._id, + workspace.name, + ]) + )[ + router.asPath + .split("/") + [router.asPath.split("/").length - 1].split("?")[0] + ] + ); + } + } + }; + putUserInWorkSpace(); }, []); useEffect(() => { try { if ( - workspaceMapping[workspaceSelected] && - workspaceMapping[workspaceSelected] !== + workspaceMapping[Number(workspaceSelected)] && + `${workspaceMapping[Number(workspaceSelected)]}` !== router.asPath - .split("/")[router.asPath.split("/").length - 1].split("?")[0] + .split("/") + [router.asPath.split("/").length - 1].split("?")[0] ) { - router.push("/dashboard/" + workspaceMapping[workspaceSelected] + "?Development"); + router.push( + "/dashboard/" + + workspaceMapping[Number(workspaceSelected)] + + "?Development" + ); localStorage.setItem( "projectData.id", - workspaceMapping[workspaceSelected] + `${workspaceMapping[Number(workspaceSelected)]}` ); } } catch (error) { @@ -212,18 +246,18 @@ export default function Layout({ children }) {