diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 1b80aa784..770fa7e83 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -6,6 +6,8 @@ jobs: docker: runs-on: ubuntu-latest steps: + - name: Checkout + uses: actions/checkout@v2 - name: Set up QEMU uses: docker/setup-qemu-action@v2 @@ -23,12 +25,13 @@ jobs: uses: docker/build-push-action@v3 with: push: true - context: /backend - tags: infisical/backend:latest + context: backend + tags: infisical/backend:test - name: Build and push frontend uses: docker/build-push-action@v3 with: push: true - context: /frontend - tags: infisical/frontend:latest + file: frontend/Dockerfile.dev + context: frontend + tags: infisical/frontend:test diff --git a/.github/workflows/helm_chart_release.yml b/.github/workflows/helm_chart_release.yml new file mode 100644 index 000000000..8f47da69d --- /dev/null +++ b/.github/workflows/helm_chart_release.yml @@ -0,0 +1,22 @@ +name: Release Helm Charts + +on: [workflow_dispatch] + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Install Helm + uses: azure/setup-helm@v3 + with: + version: v3.10.0 + - name: Install python + uses: actions/setup-python@v4 + - name: Install Cloudsmith CLI + run: pip install --upgrade cloudsmith-cli + - name: Build and push helm package to Cloudsmith + run: cd helm-charts && sh upload-to-cloudsmith.sh + env: + CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }} \ No newline at end of file diff --git a/.github/workflows/release_build.yml b/.github/workflows/release_build.yml index aea82a9d2..695b0ea24 100644 --- a/.github/workflows/release_build.yml +++ b/.github/workflows/release_build.yml @@ -1,4 +1,4 @@ -name: goreleaser +name: Go releaser on: push: diff --git a/README.md b/README.md index 169d51509..4215a9fa8 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@

Slack | - Infisical Cloud | + Infisical Cloud | Self-Hosting | Docs | Website @@ -21,6 +21,9 @@ PRs welcome! + + git commit activity + Slack community channel @@ -30,12 +33,12 @@ **[Infisical](https://infisical.com)** is an open source, E2EE tool to help teams manage and sync environment variables across their development workflow and infrastructure. It's designed to be simple and take minutes to get going. -- **User-Friendly Dashboard** to manage your team's environment variables within projects +- **[User-Friendly Dashboard](https://infisical.com/docs/getting-started/dashboard/project)** to manage your team's environment variables within projects - **[Language-Agnostic CLI](https://infisical.com/docs/cli/overview)** that pulls and injects environment variables into your local workflow - **[Complete control over your data](https://infisical.com/docs/self-hosting/overview)** - host it yourself on any infrastructure - **Navigate Multiple Environments** per project (e.g. development, staging, production, etc.) - **Personal/Shared** scoping for environment variables -- **[Integrations](https://infisical.com/docs/integrations/heroku)** with CI/CD and production infrastructure (Heroku available, more coming soon) +- **[Integrations](https://infisical.com/docs/integrations/overview)** with CI/CD and production infrastructure (Heroku available, more coming soon) - πŸ”œ **1-Click Deploy** to Digital Ocean and Heroku - πŸ”œ **Authentication/Authorization** for projects (read/write controls soon) - πŸ”œ **Automatic Secret Rotation** @@ -51,15 +54,13 @@ To quickly get started, visit our [get started guide](https://infisical.com/docs ## What's cool about this? -Infisical is simple, E2EE, and (soon to be) complete. +Infisical makes secret management simple and end-to-end encrypted by default. We're on a mission to make it more accessible to all developers, not just security teams. According to a [report](https://www.ekransystem.com/en/blog/secrets-management) in 2019, only 10% of organizations use secret management solutions despite all using digital secrets to some extent. -We're on a mission to make secret management more accessible to everyone β€” that means building for developers, not just security teams. - If you care about efficiency and security, then Infisical is right for you. -Need any integrations or want a new feature? Feel free to [create an issue](https://github.com/Infisical/infisical/issues) or [contribute](https://infisical.com/docs/contributing/overview) directly to the repository. +We are currently working hard to make Infisical more extensive. Need any integrations or want a new feature? Feel free to [create an issue](https://github.com/Infisical/infisical/issues) or [contribute](https://infisical.com/docs/contributing/overview) directly to the repository. ## Contributing @@ -69,9 +70,10 @@ Not sure where to get started? [Book a free, non-pressure pairing sessions with ## Community & Support -- [GitHub Discussions](https://github.com/Infisical/infisical/discussions) for help with building and discussion. -- [GitHub Issues](https://github.com/Infisical/infisical-cli/issues) for any bugs and errors you encounter using Infisical. -- [Community Slack](https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g) for hanging out with the community and quick communication with the team. +- [Slack](https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g) (For live discussion with the community and the Infisical team) +- [GitHub Discussions](https://github.com/Infisical/infisical/discussions) (For help with building and deeper conversations about features) +- [GitHub Issues](https://github.com/Infisical/infisical-cli/issues) (For any bugs and errors you encounter using Infisical) +- [Twitter](https://twitter.com/infisical) (Get news fast) ## Status @@ -89,23 +91,179 @@ Infisical officially launched as v.1.0 on November 21st, 2022. However, a lot of ## Integrations -We're currently setting the foundation and building integrations so secrets can be synced everywhere. Any help is welcome! :) +We're currently setting the foundation and building [integrations](https://infisical.com/docs/integrations/overview) so secrets can be synced everywhere. Any help is welcome! :) + + + + + + + + + + +
Platforms Frameworks
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + βœ”οΈ Docker + + + + βœ”οΈ Docker Compose + + + + βœ”οΈ Heroku + +
+ πŸ”œ Vercel (https://github.com/Infisical/infisical/issues/60) + + πŸ”œ GitLab CI/CD + + πŸ”œ Fly.io +
+ πŸ”œ AWS + + πŸ”œ GitHub Actions (https://github.com/Infisical/infisical/issues/54) + + πŸ”œ Railway +
+ πŸ”œ GCP + + πŸ”œ Kubernetes + + πŸ”œ CircleCI +
+ πŸ”œ Jenkins + + πŸ”œ Digital Ocean + + πŸ”œ Azure +
+ πŸ”œ TravisCI + + πŸ”œ Netlify (https://github.com/Infisical/infisical/issues/55) +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + βœ”οΈ React + + + + βœ”οΈ Express + +
+ + βœ”οΈ Gatsby + + + + βœ”οΈ Flask + +
+ + βœ”οΈ Django + + + + βœ”οΈ Laravel + +
+ + βœ”οΈ NestJS + + + + βœ”οΈ Remix + +
+ + βœ”οΈ Next.js + + + + βœ”οΈ Vite + +
+ + βœ”οΈ Ruby on Rails + + + + βœ”οΈ Vue + +
+ +
-- [x] [Docker](https://infisical.com/docs/integrations/docker) -- [x] [Docker Compose](https://infisical.com/docs/integrations/docker-compose) -- [x] [Heroku](https://infisical.com/docs/integrations/heroku) -- [ ] Vercel -- [ ] Kubernetes -- [ ] AWS -- [ ] GCP -- [ ] Azure -- [ ] Digital Ocean -- [ ] GitLab -- [ ] CircleCI ## Open-source vs. paid -This repo is entirely MIT licensed, with the exception of the `ee` directory which will contain premium enterprise features requring a Infisical license in the future. We're currently focused on developing non-enterprise offerings first that should suit most use-cases. +This repo is entirely MIT licensed, with the exception of the `ee` directory which will contain premium enterprise features requiring a Infisical license in the future. We're currently focused on developing non-enterprise offerings first that should suit most use-cases. ## Security @@ -119,4 +277,4 @@ Looking to report a security vulnerability? Please don't post about it in GitHub - + diff --git a/backend/package.json b/backend/package.json index 9dfe476e9..2bd296d9c 100644 --- a/backend/package.json +++ b/backend/package.json @@ -22,8 +22,6 @@ "query-string": "^7.1.1", "rimraf": "^3.0.2", "stripe": "^10.7.0", - "swagger-jsdoc": "^6.2.5", - "swagger-ui-express": "^4.6.0", "tweetnacl": "^1.0.3", "tweetnacl-util": "^0.15.1", "typescript": "^4.8.4" diff --git a/backend/src/helpers/signup.ts b/backend/src/helpers/signup.ts index 410c442b3..e26b9b360 100644 --- a/backend/src/helpers/signup.ts +++ b/backend/src/helpers/signup.ts @@ -106,7 +106,7 @@ const initializeDefaultOrg = async ({ // initialize a default workspace inside the new organization const workspace = await createWorkspace({ - name: `${user.firstName}'s Project`, + name: `Example Project`, organizationId: organization._id.toString() }); diff --git a/backend/src/index.ts b/backend/src/index.ts index 1bc596865..a5ae44969 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -4,8 +4,6 @@ import cors from 'cors'; import cookieParser from 'cookie-parser'; import mongoose from 'mongoose'; import dotenv from 'dotenv'; -import swaggerJSDoc from 'swagger-jsdoc'; -import swaggerUi from 'swagger-ui-express'; dotenv.config(); import * as Sentry from '@sentry/node'; @@ -68,39 +66,6 @@ if (NODE_ENV === 'production') { app.use(helmet()); } -if (NODE_ENV === 'development') { - const swaggerDefinition = { - openapi: '3.0.0', - info: { - title: 'Infisical API', - version: '1.0.0', - description: 'Infisical is an open-source, E2EE tool to sync environment variables across your team and infrastructure.', - license: { - name: 'License', - url: 'https://github.com/Infisical/infisical/blob/main/LICENSE' - }, - contact: { - name: 'Infisical', - url: 'https://infisical.com' - }, - }, - servers: [ - { - url: 'http://localhost:8080/api/v1', - description: 'Development server' - } - ] - } - - const options = { - swaggerDefinition, - apis: ['./src/routes/*.ts'] - } - - const swaggerSpec = swaggerJSDoc(options); - app.use('/api/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec)); -} - app.use(express.json()); // routers diff --git a/backend/src/routes/auth.ts b/backend/src/routes/auth.ts index 52cd6ac48..43a57fe7e 100644 --- a/backend/src/routes/auth.ts +++ b/backend/src/routes/auth.ts @@ -11,53 +11,6 @@ router.post( authController.getNewToken ); -/** - * @swagger - * /auth/login1: - * post: - * summary: Return server's public key and user's salt for login via SRP. - * description: As part of the 1st step of secure remote password (SRP), return server's public key and user's salt. - * requestBody: - * required: true - * content: - * application/json: - * schema: - * type: object - * properties: - * email: - * type: string - * description: Email of user - * example: 'johndoe@gmail.com' - * clientPublicKey: - * type: string - * description: Client-side SRP-generated public key - * example: 'b13dc83cb...' - * responses: - * 200: - * description: Object containing the server's public key and user's salt. - * content: - * application/json: - * schema: - * type: object - * properties: - * serverPublicKey: - * type: string - * description: Server-side SRP-generated public key - * example: 'b13dc83cb...' - * salt: - * type: string - * description: User's salt - * example: 'dc1ac694f...' - * 400: - * content: - * application/json: - * schema: - * type: object - * properties: - * message: - * type: string - * example: 'Failed to start authentication process' - */ router.post( '/login1', loginLimiter, diff --git a/cli/packages/cmd/root.go b/cli/packages/cmd/root.go index 826ccb4df..42d85e5b2 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.5", + Version: "0.1.6", } // Execute adds all child commands to the root command and sets flags appropriately. diff --git a/docs/cli/overview.mdx b/docs/cli/overview.mdx index bbe86d379..8411b8e75 100644 --- a/docs/cli/overview.mdx +++ b/docs/cli/overview.mdx @@ -95,3 +95,9 @@ The Infisical CLI provides a way to inject environment variables from the platfo + +## Log in to the Infisical CLI + +```bash +infisical login +``` diff --git a/docs/cli/token.mdx b/docs/cli/token.mdx index 043f07431..5b3d2fd6b 100644 --- a/docs/cli/token.mdx +++ b/docs/cli/token.mdx @@ -12,8 +12,8 @@ The CLI looks out for an environment variable called the `INFISICAL_TOKEN` which A common use-case is to use the Infisical Token to fetch environment variables with Docker. More specifically, a token can be passed to a container as an environment variable for the CLI to authenticate and pull its corresponding secrets. Check out the integration guides for that: -- [Docker](../../integrations/docker) -- [Docker Compose](../../integrations/docker-compose) +- [Docker](../../integrations/platforms/docker) +- [Docker Compose](../../integrations/platforms/docker-compose) Once the token is expired, the CLI using it will no longer be able to make diff --git a/docs/cli/usage.mdx b/docs/cli/usage.mdx index ea7180068..4237f8154 100644 --- a/docs/cli/usage.mdx +++ b/docs/cli/usage.mdx @@ -4,12 +4,6 @@ title: "Usage" Prerequisite: [Install the CLI](/cli/overview) -## Log in to the Infisical CLI - -```bash -infisical login -``` - ## Initialize Infisical for your project ```bash @@ -29,11 +23,11 @@ infisical run -- [your application start command] Options you can specify: -| Option | Description | Default value | -| ------------- | ----------------------------------------------------------------------------------------------------------- | ------------- | -| `--env` | Used to set the environment that secrets are pulled from. Accepted values: `dev`, `staging`, `test`, `prod` | `dev` | -| `--projectId` | Used to link a local project to the platform (required only if injecting via the service token method) | `None` | -| `--expand` | Parse shell parameter expansions in your secrets (e.g., `${DOMAIN}`) | `true` | +| Option | Description | Default value | +| ------------- | ----------------------------------------------------------------------------------------------------------- | ------------- | +| `--env` | Used to set the environment that secrets are pulled from. Accepted values: `dev`, `staging`, `test`, `prod` | `dev` | +| `--projectId` | Used to link a local project to the platform (required only if injecting via the service token method) | `None` | +| `--expand` | Parse shell parameter expansions in your secrets (e.g., `${DOMAIN}`) | `true` | ## Examples: diff --git a/docs/contributing/FAQ.mdx b/docs/contributing/FAQ.mdx new file mode 100644 index 000000000..be65f5ca7 --- /dev/null +++ b/docs/contributing/FAQ.mdx @@ -0,0 +1,16 @@ +--- +title: "Frequently Asked Questions" +description: "Have any questions? [Join our Slack community](https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g)." +--- + +## Problem with SMTP + +You can normally populate `SMTP_USERNAME` and `SMTP_PASSWORD` with your usual login and password (you could also create a 'burner' email). Sometimes, there still are problems. + +You can go to your Gmail account settings > security and enable β€œless secure apps”. This would allow Infisical to use your Gmail to send emails. + +If it still doesn't work, [this](https://stackoverflow.com/questions/72547853/unable-to-send-email-in-c-sharp-less-secure-app-access-not-longer-available/72553362#72553362) should help. + +## `MONGO_URL` issues + +Your `MONGO_URL` should be something like `mongodb://root:example@mongo:27017/?authSource=admin`. If you want to change it (not recommended), you should make sure that you keep this URL in line with `MONGO_USERNAME=root` and `MONGO_PASSWORD=example`. \ No newline at end of file diff --git a/docs/contributing/developing.mdx b/docs/contributing/developing.mdx index cca804a9c..b6c25861c 100644 --- a/docs/contributing/developing.mdx +++ b/docs/contributing/developing.mdx @@ -16,7 +16,7 @@ cd infisical ## Set up environment variables -Tweak the `.env` according to your preferences. Refer to the available [environment variables](envars). +Tweak the `.env` according to your preferences. Refer to the available [environment variables](/self-hosting/configuration/envars). ```bash cp .env.example .env diff --git a/docs/getting-started/dashboard/create-account.mdx b/docs/getting-started/dashboard/create-account.mdx index ea412c1e7..ddc1654e3 100644 --- a/docs/getting-started/dashboard/create-account.mdx +++ b/docs/getting-started/dashboard/create-account.mdx @@ -8,24 +8,7 @@ If you're using a self-hosted installation, follow the [setup](/self-hosting/ove ## Infisical Cloud -**Step 1:** Open [infisical.com](https://infisical.com/) and click on either "Try Infisical for free" or "Start for free" to head to the signup sequence. - -![title](../../images/landing-page.png) - -**Step 2:** Fill out the signup sequence. - -![signup start](../../images/signup-box.png) -![signup one-time password](../../images/signup-otp.png) -![signup complete account](../../images/signup-complete-account.png) - -You'll be prompted to fill out some required fields to set up your account. - -| Field | Description | -| ---------- | --------------------------- | -| Email | Enter a valid email address | -| First name | Your first name | -| Last name | Your last name | -| Password | Password | +Open [infisical.com](https://infisical.com/) and click on either "Try Infisical for free" or "Start for free" to complete the signup sequence. Once you've done that, you'll be taken to the dashboard where we've populated some default environment variables for demonstration. diff --git a/docs/getting-started/dashboard/integrations.mdx b/docs/getting-started/dashboard/integrations.mdx index 193cde84b..95541ca38 100644 --- a/docs/getting-started/dashboard/integrations.mdx +++ b/docs/getting-started/dashboard/integrations.mdx @@ -2,12 +2,10 @@ title: "Integrations" --- -We’re still early with integrations but you’ll be able to sync environment variables across your entire infrastructure from local development to CI/CD and production. +Integrations allow environment variables to be synced across your entire infrastructure from local development to CI/CD and production. + +We're still early with integrations, but expect more soon. ![integrations](../../images/project-integrations.png) -Check out integrations: - -- [Heroku](/integrations/heroku) -- [Docker](/integrations/docker) -- [Docker Compose](/integrations/docker-compose) +Check out our [integrations](/integrations/overview). diff --git a/docs/getting-started/dashboard/organization.mdx b/docs/getting-started/dashboard/organization.mdx index 7cbd642fe..c58922daa 100644 --- a/docs/getting-started/dashboard/organization.mdx +++ b/docs/getting-started/dashboard/organization.mdx @@ -2,15 +2,12 @@ title: "Organization" --- -By default, Infisical creates an organization under your name such as "John's Organization." To make changes and add members to your organization, head to your organization settings. +An organization houses projects and members. -![organization name selected](../../images/dashboard-name-selected.png) +By default, Infisical creates an organization under your name. You can manage your organization in your organization settings. ![organization name modal open](../../images/dashboard-name-modal-organization.png) - -![organization page](../../images/organization.png) - -Feel free to change the name of your organization. +![organization name modal open](../../images/organization.png) ## Members @@ -19,9 +16,12 @@ Members of an organization can create and add other members to projects within t To add a member to your organization, scroll down to the "Organization Members" section and invite the member via email. They'll receive an email to confirm their organization invitation. If the member is an existing user on the platform, they will be automatically added to the organization. ![organization members](../../images/organization-members.png) -![organization members add](../../images/organization-members-add.png) -Note that access to projects must be provisioned to new members after they've accepted their invitation, and they will not be added to any projects by default. + + Note that access to projects must be provisioned to new members after they've + accepted their organization invitation, and they will not be added to any + projects by default. + ## Incident contacts @@ -30,4 +30,3 @@ Incident contacts of an organization are alerted if anything abnormal is detecte To add an incident contact to your organization, scroll down to the "Incident Contacts" section and add their email. ![organization incident contacts](../../images/organization-ic.png) -![organization incident contacts add](../../images/organization-ic-add.png) diff --git a/docs/getting-started/dashboard/project.mdx b/docs/getting-started/dashboard/project.mdx index b385f09d8..aa64d635b 100644 --- a/docs/getting-started/dashboard/project.mdx +++ b/docs/getting-started/dashboard/project.mdx @@ -2,7 +2,7 @@ title: "Project" --- -A project houses environment variables for an application or service. +A project houses environment variables for an application. ## Dashboard @@ -33,20 +33,14 @@ Every environment variable is classified as either personal or shared. You can toggle the classification of an environment variable by pressing on its settings: -![project variable toggle](../../images/project-envar-toggle.png) - ![project variable toggle open](../../images/project-envar-toggle-open.png) -![project variable toggle moved](../../images/project-envar-toggle-moved.png) - ### Search You can search for any environment variable by its key. ![project search](../../images/project-search.png) -![project search typed](../../images/project-search-typed.png) - ### Sort You can sort environment variables alphabetically by their keys. @@ -59,8 +53,6 @@ You can hide or un-hide the values of your environment variables. By default, th ![project hide](../../images/project-hide.png) -![project unhide](../../images/project-hide.png) - ### Download as .env You can download your environment variables back in a .env file. diff --git a/docs/getting-started/dashboard/token.mdx b/docs/getting-started/dashboard/token.mdx index 121578417..85f46b799 100644 --- a/docs/getting-started/dashboard/token.mdx +++ b/docs/getting-started/dashboard/token.mdx @@ -2,20 +2,15 @@ title: "Infisical Token" --- -An Infisical Token is needed to authenticate the CLI when there isn't an easy way to manually type in your login credentials to sync environment variables to your applications. +An Infisical Token is needed to authenticate the CLI when there isn't an easy way to input your login credentials. -It grants read-only access to a particular environment and project for a specified amount of time. +It's useful for the [Docker](/integrations/platforms/docker) and [Docker Compose](/integrations/platforms/docker-compose) integrations. -This is useful in the following contexts: - -- [Docker](/integrations/docker)/[Docker-Compose](/integrations/docker-compose) integration: An Infisical Token can be passed to a Docker container as an environment variable for the CLI to authenticate and pull its corresponding secrets. - -## Generate an Infisical Token - -It's possible to generate an Infisical token in the settings of a project. +It's possible to generate the token in the settings of a project. ![token add](../../images/project-token-add.png) -![token name](../../images/project-token-name.png) - -![token added](../../images/project-token-added.png) + + The token grants read-only access to a particular environment and project for + a specified amount of time. + diff --git a/docs/getting-started/introduction.mdx b/docs/getting-started/introduction.mdx index 5f5d2a44d..72b86bf05 100644 --- a/docs/getting-started/introduction.mdx +++ b/docs/getting-started/introduction.mdx @@ -27,7 +27,7 @@ Start syncing environment variables with [Infisical Cloud](https://app.infisical Learn how to configure and deploy Infisical. + Note that for environment variables to be exposed to the client, you'll have + to prefix them with `GATSBY_`. Read more about that + [here](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser). + diff --git a/docs/integrations/frameworks/laravel.mdx b/docs/integrations/frameworks/laravel.mdx new file mode 100644 index 000000000..0dfb219aa --- /dev/null +++ b/docs/integrations/frameworks/laravel.mdx @@ -0,0 +1,10 @@ +--- +title: "Laravel" +--- + +Prerequisites: + +- Set up and add envars to [Infisical Cloud](https://app.infisical.com) +- [Install the CLI](/cli/overview) + +Instructions coming soon. diff --git a/docs/integrations/frameworks/nestjs.mdx b/docs/integrations/frameworks/nestjs.mdx new file mode 100644 index 000000000..8283b1175 --- /dev/null +++ b/docs/integrations/frameworks/nestjs.mdx @@ -0,0 +1,50 @@ +--- +title: "NestJS" +--- + +Prerequisites: + +- Set up and add envars to [Infisical Cloud](https://app.infisical.com) +- [Install the CLI](/cli/overview) + +## Initialize Infisical for your [NestJS](https://nestjs.com) app + +```bash +# move to your Next.js app +cd /path/to/project + +# initialize infisical +infisical init +``` + +## Modify the start script in your `package.json` + +```json +... +"scripts": { + "prebuild": "rimraf dist", + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "start": "infisical run -- nest start", + "start:dev": "infisical run -- nest start --watch", + "start:debug": "nest start --debug --watch", + "start:prod": "node dist/main", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "test": "jest", + "test:watch": "jest --watch", + "test:cov": "jest --coverage", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", + "test:e2e": "jest --config ./test/jest-e2e.json" +} +... +``` + +## Start your server with environment variables injected + +```bash +npm run start + +# or start development server + +npm run start:dev +``` diff --git a/docs/integrations/frameworks/nextjs.mdx b/docs/integrations/frameworks/nextjs.mdx new file mode 100644 index 000000000..a70cf6e41 --- /dev/null +++ b/docs/integrations/frameworks/nextjs.mdx @@ -0,0 +1,46 @@ +--- +title: "Next.js" +--- + +Prerequisites: + +- Set up and add envars to [Infisical Cloud](https://app.infisical.com) +- [Install the CLI](/cli/overview) + +## Initialize Infisical for your [Next.js](https://nextjs.org) app + +```bash +# move to your Next.js app +cd /path/to/project + +# initialize infisical +infisical init +``` + +## Modify the start script in your `package.json` + +```json +... +"scripts": { + "dev": "infisical run -- next dev", + "build": "infisical run -- next build", + "start": "infisical run -- next start" +} +... +``` + +## Start your server with environment variables injected + +```bash +npm run build && npm run start + +# or start development server + +npm run dev +``` + + + Note that for environment variables to be exposed to the client, you'll have + to prefix them with `NEXT_PUBLIC_`. Read more about that + [here](https://nextjs.org/docs/basic-features/environment-variables). + diff --git a/docs/integrations/frameworks/nuxt.mdx b/docs/integrations/frameworks/nuxt.mdx new file mode 100644 index 000000000..901ec35a5 --- /dev/null +++ b/docs/integrations/frameworks/nuxt.mdx @@ -0,0 +1,36 @@ +--- +title: "Nuxt" +--- + +Prerequisites: + +- Set up and add envars to [Infisical Cloud](https://app.infisical.com) +- [Install the CLI](/cli/overview) + +## Initialize Infisical for your [Nuxt](https://nuxtjs.org) app + +```bash +# move to your Nuxt app +cd /path/to/project + +# initialize infisical +infisical init +``` + +## Modify the start script in your `package.json` + +```json +... +"scripts": { + "dev": "infisical run -- nuxt", + "build": "infisical run -- nuxt build", + "start": "infisical run -- nuxt start" +} +... +``` + +## Start your development server with environment variables injected + +```bash +npm run dev +``` diff --git a/docs/integrations/frameworks/rails.mdx b/docs/integrations/frameworks/rails.mdx new file mode 100644 index 000000000..7e08bffa7 --- /dev/null +++ b/docs/integrations/frameworks/rails.mdx @@ -0,0 +1,24 @@ +--- +title: "Ruby on Rails" +--- + +Prerequisites: + +- Set up and add envars to [Infisical Cloud](https://app.infisical.com) +- [Install the CLI](/cli/overview) + +## Initialize Infisical for your [Rails](https://rubyonrails.org) app + +```bash +# move to your Rails app +cd /path/to/project + +# initialize infisical +infisical init +``` + +## Start your server with environment variables injected + +```bash +infisical run -- bin/rails server +``` diff --git a/docs/integrations/frameworks/react.mdx b/docs/integrations/frameworks/react.mdx new file mode 100644 index 000000000..950505432 --- /dev/null +++ b/docs/integrations/frameworks/react.mdx @@ -0,0 +1,37 @@ +--- +title: "React" +--- + +Prerequisites: + +- Set up and add envars to [Infisical Cloud](https://app.infisical.com) +- [Install the CLI](/cli/overview) + +## Initialize Infisical for your [Create React App](https://create-react-app.dev) + +```bash +# move to your React app +cd /path/to/project + +# initialize infisical +infisical init +``` + +## Modify the start script in your `package.json` + +```json +... +"scripts": { + "start": "infisical run -- react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" +} +... +``` + +## Start your server with environment variables injected + +```bash +npm start +``` diff --git a/docs/integrations/frameworks/remix.mdx b/docs/integrations/frameworks/remix.mdx new file mode 100644 index 000000000..aa9956005 --- /dev/null +++ b/docs/integrations/frameworks/remix.mdx @@ -0,0 +1,41 @@ +--- +title: "Remix" +--- + +Prerequisites: + +- Set up and add envars to [Infisical Cloud](https://app.infisical.com) +- [Install the CLI](/cli/overview) + +## Initialize Infisical for your [Remix](https://remix.run) app + +```bash +# move to your Vue app +cd /path/to/project + +# initialize infisical +infisical init +``` + +## Modify the start script in your `package.json` + +```json +... +"scripts": { + ... + "dev": "infisical run -- run-p dev:*", + "start": "infisical run -- remix-serve build" + ... +} +... +``` + +## Start your server with environment variables injected + +```bash +npm run build && npm run start + +# or start development server + +npm run dev +``` diff --git a/docs/integrations/frameworks/vite.mdx b/docs/integrations/frameworks/vite.mdx new file mode 100644 index 000000000..251924ca7 --- /dev/null +++ b/docs/integrations/frameworks/vite.mdx @@ -0,0 +1,43 @@ +--- +title: "Vite" +--- + +Prerequisites: + +- Set up and add envars to [Infisical Cloud](https://app.infisical.com) +- [Install the CLI](/cli/overview) + +## Initialize Infisical for your [Vite](https://vitejs.dev) app + +```bash +# move to your Vite app +cd /path/to/project + +# initialize infisical +infisical init +``` + +## Modify the start script in your `package.json` + +```json +... +"scripts": { + "dev": "infisical run -- vite", + "build": "infisical run -- vite build", + "preview": "infisical run -- vite preview" +} +... +``` + +## Start your development server with environment variables injected + +```bash +npm run dev +``` + + + Note that for environment variables to be exposed to the client, you'll have + to prefix them with `VITE_` and export them from the `vite.config.js` file. + Read more about that [here](https://vitejs.dev/guide/env-and-mode.html) and + [here](https://main.vitejs.dev/config). + diff --git a/docs/integrations/frameworks/vue.mdx b/docs/integrations/frameworks/vue.mdx new file mode 100644 index 000000000..15c4a12cf --- /dev/null +++ b/docs/integrations/frameworks/vue.mdx @@ -0,0 +1,42 @@ +--- +title: "Vue" +--- + +Prerequisites: + +- Set up and add envars to [Infisical Cloud](https://app.infisical.com) +- [Install the CLI](/cli/overview) + +## Initialize Infisical for your [Vue](https://vuejs.org) app + +```bash +# move to your Vue app +cd /path/to/project + +# initialize infisical +infisical init +``` + +## Modify the start script in your `package.json` + +```json +... +"scripts": { + "serve": "infisical run -- vue-cli-service serve", + "build": "infisical run -- vue-cli-service build", + "lint": "infisical run -- vue-cli-service lint" +} +... +``` + +## Start your development server with environment variables injected + +```bash +npm run serve +``` + + + Note that for environment variables to be exposed to the client, you'll have + to prefix them with `VUE_APP` Read more about that + [here](https://cli.vuejs.org/guide/mode-and-env.html). + diff --git a/docs/integrations/overview.mdx b/docs/integrations/overview.mdx index 4f4a2db3e..17c7a7f72 100644 --- a/docs/integrations/overview.mdx +++ b/docs/integrations/overview.mdx @@ -2,23 +2,37 @@ title: "Overview" --- -Integrations allow environment variables to be synced across your entire infrastructure from local development to CI/CD and production. +Integrations allow environment variables to be synced from Infisical into your local development workflow, CI/CD pipelines, and production infrastructure. Missing an integration? Throw in a [request](https://github.com/Infisical/infisical/issues). -| Integration | Status | -| ---------------------------------------------- | ----------- | -| [Docker](/integrations/docker) | Available | -| [Docker-Compose](/integrations/docker-compose) | Available | -| [Heroku](/integrations/heroku) | Available | -| Kubernetes | Coming soon | -| Vercel | Coming soon | -| AWS | Coming soon | -| GCP | Coming soon | -| Azure | Coming soon | -| DigitalOcean | Coming soon | -| GitLab | Coming soon | -| CircleCI | Coming soon | -| TravisCI | Coming soon | -| GitHub Actions | Coming soon | -| Jenkins | Coming soon | +| Integration | Type | Status | +| -------------------------------------------------------- | --------- | ----------- | +| [Docker](/integrations/platforms/docker) | Platform | Available | +| [Docker-Compose](/integrations/platforms/docker-compose) | Platform | Available | +| Kubernetes | Platform | Coming soon | +| [Heroku](/integrations/cloud/heroku) | Cloud | Available | +| Vercel | Cloud | Coming soon | +| AWS | Cloud | Coming soon | +| GCP | Cloud | Coming soon | +| Azure | Cloud | Coming soon | +| DigitalOcean | Cloud | Coming soon | +| GitLab | CI/CD | Coming soon | +| CircleCI | CI/CD | Coming soon | +| TravisCI | CI/CD | Coming soon | +| GitHub Actions | CI/CD | Coming soon | +| Jenkins | CI/CD | Coming soon | +| [React](/integrations/frameworks/react) | Framework | Available | +| [Vue](/integrations/frameworks/vue) | Framework | Available | +| [Express](/integrations/frameworks/express) | Framework | Available | +| [Next.js](/integrations/frameworks/nextjs) | Framework | Available | +| [NestJS](/integrations/frameworks/nestjs) | Framework | Available | +| [Nuxt](/integrations/frameworks/nuxt) | Framework | Available | +| [Gatsby](/integrations/frameworks/gatsby) | Framework | Available | +| [Remix](/integrations/frameworks/remix) | Framework | Available | +| [Vite](/integrations/frameworks/vite) | Framework | Available | +| [Fiber](/integrations/frameworks/fiber) | Framework | Coming soon | +| [Django](/integrations/frameworks/django) | Framework | Available | +| [Flask](/integrations/frameworks/flask) | Framework | Available | +| [Laravel](/integrations/frameworks/laravel) | Framework | Coming soon | +| [Ruby on Rails](/integrations/frameworks/rails) | Framework | Available | diff --git a/docs/integrations/docker-compose.mdx b/docs/integrations/platforms/docker-compose.mdx similarity index 84% rename from docs/integrations/docker-compose.mdx rename to docs/integrations/platforms/docker-compose.mdx index 97ce0aba0..9e0f90e55 100644 --- a/docs/integrations/docker-compose.mdx +++ b/docs/integrations/platforms/docker-compose.mdx @@ -2,11 +2,13 @@ title: "Docker Compose" --- -### Step 1: Add CLI to your Dockerfile +The Docker Compose integration enables you to inject environment variables from Infisical into the containers defined in your compose file. -Follow steps 1 through 3 on our [guide to configure Infisical CLI](../getting-started/cli/installation) in your Dockerfile. +## Add the CLI to your Dockerfile(s) -### Step 2: Generate Infisical Token +Follow steps 1 through 3 on our [guide to configure Infisical CLI](../integrations/platforms/docker) in your Dockerfile. + +## Generate Infisical Token In order for Infisical CLI to authenticate and retrieve your project's secrets without exposing your login credentials, you must generate a Infisical Token. To learn how, visit [Infisical Token](../getting-started/cli/infisical-token). Once you have generated the token, keep it handy. @@ -16,7 +18,7 @@ To learn how, visit [Infisical Token](../getting-started/cli/infisical-token). O have to generate a Infisical Token for each service. -### Step 3: Tell Docker Compose your Infisical Token +## Tell Docker Compose your Infisical Token For each service you want to inject secrets into, set an environment variable called `INFISICAL_TOKEN` equal to a helpful identifier variable. This will ensure that you can set Infisical Tokens for multiple services. @@ -41,7 +43,7 @@ services: - INFISICAL_TOKEN=${INFISICAL_TOKEN_FOR_API} ``` -### 4: Export shell variables +## Export shell variables Next, set the shell variables you defined in your compose file. This can be done manually or via your CI/CD environment. Once done, it will be used to populate the corresponding `INFISICAL_TOKEN` in your Docker Compose file. diff --git a/docs/integrations/docker.mdx b/docs/integrations/platforms/docker.mdx similarity index 69% rename from docs/integrations/docker.mdx rename to docs/integrations/platforms/docker.mdx index aad94ae6c..6f08c9ec8 100644 --- a/docs/integrations/docker.mdx +++ b/docs/integrations/platforms/docker.mdx @@ -2,7 +2,9 @@ title: "Docker" --- -## Step 1: Add CLI to your Dockerfile +Infisical can be used in a Dockerfile to inject environment variables into a Docker container. + +## Add the CLI to your Dockerfile @@ -29,14 +31,10 @@ title: "Docker" -## Step 2: Generate Infisical Token - -[Generate an Infisical Token](../../getting-started/dashboard/token) and keep it handy. - -## Step 3: Set start command of your container +## Modify the start command in your Dockerfile ```dockerfile -CMD ["infisical", "--env=[your-project-env-name]", "projectId=[your-project-id]", "run", "---", ""] +CMD ["infisical", "--env=[env]", "projectId=[projectId]", "run", "---", "[your application start command]"] # example CMD ["infisical", "--env=prod", "projectId=62faf98ae0b05e83239b5da41", "run", "---", "npm run start"] @@ -49,12 +47,14 @@ Required options: | `--env` | Used to set the environment that secrets are pulled from. Accepted values: `dev`, `staging`, `test`, `prod` | `dev` | | `--projectId` | Used to link a local project to the platform | `None` | -## Step 4: Feed Docker your Infisical Token +## Generate an Infisical Token -The CLI looks out for an environment variable called the `INFISICAL_TOKEN` which you can set depending on where you run the CLI. If `INFISICAL_TOKEN` is detected by the CLI, it will authenticate and retrieve the environment variables which the token is authorized for. +[Generate an Infisical Token](../../getting-started/dashboard/token) and keep it handy. + +## Feed Docker your Infisical Token + +The CLI looks out for an environment variable called `INFISICAL_TOKEN`. If the token is detected, the CLI will authenticate, retrieve, and inject the environment variables which the token is authorized for. ```bash - docker run --env INFISICAL_TOKEN=[the-token-you-got-from-step-2]... + docker run --env INFISICAL_TOKEN=[token]... ``` - -Note: `INFISICAL_TOKEN` is the token you generated in step 2. diff --git a/docs/mint.json b/docs/mint.json index baea4bbe3..1d5246680 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -27,90 +27,147 @@ "url": "https://app.infisical.com/signup" }, "anchors": [ + { + "name": "Security", + "icon": "shield-halved", + "url": "security" + }, + { + "name": "Self-hosting", + "icon": "server", + "url": "self-hosting" + }, + { + "name": "Integrations", + "icon": "plug", + "url": "integrations" + }, + { + "name": "Contributing", + "icon": "code", + "url": "contributing" + }, { "name": "Blog", "icon": "newspaper", "url": "https://blog.infisical.com/" + }, + { + "name": "Slack", + "icon": "slack", + "url": "https://join.slack.com/t/infisical-users/shared_invite/zt-1kovn1q6p-p5fvJo7o083naouDkOzgZQ" + }, + { + "name": "GitHub", + "icon": "github", + "url": "https://github.com/Infisical/infisical" } ], "navigation": [ { - "group": " ", + "group": "Overview", "pages": [ + "getting-started/introduction", + "getting-started/quickstart", + "getting-started/features" + ] + }, + { + "group": "Platform", + "pages": [ + "getting-started/dashboard/organization", + "getting-started/dashboard/project", + "getting-started/dashboard/integrations", + "getting-started/dashboard/token" + ] + }, + { + "group": "CLI", + "pages": [ + "cli/overview", + "cli/usage", { - "group": "Overview", + "group": "Reference", "pages": [ - "getting-started/introduction", - "getting-started/quickstart", - "getting-started/features" - ] - }, - { - "group": "Security", - "pages": [ - "security/overview", - "security/data-model", - "security/mechanics" - ] - }, - { - "group": "Web UI", - "pages": [ - "getting-started/dashboard/create-account", - "getting-started/dashboard/organization", - "getting-started/dashboard/project", - "getting-started/dashboard/integrations", - "getting-started/dashboard/token" - ] - }, - { - "group": "CLI", - "pages": [ - "cli/overview", - "cli/usage", - { - "group": "Reference", - "pages": [ - "cli/reference/commands", - "cli/reference/login", - "cli/reference/init", - "cli/reference/run" - ] - } - ] - }, - { - "group": "Integrations", - "pages": [ - "integrations/overview", - "integrations/heroku", - "integrations/docker", - "integrations/docker-compose" - ] - }, - { - "group": "Self-hosting", - "pages": [ - "self-hosting/overview", - { - "group": "Deployments", - "pages": ["self-hosting/deployments/linux"] - }, - { - "group": "Configuration", - "pages": ["self-hosting/configuration/envars"] - } - ] - }, - { - "group": "Contributing", - "pages": [ - "contributing/overview", - "contributing/code-of-conduct", - "contributing/developing" + "cli/reference/commands", + "cli/reference/login", + "cli/reference/init", + "cli/reference/run" ] } ] + }, + { + "group": "Self-hosting", + "pages": [ + "self-hosting/overview", + { + "group": "Deployments options", + "pages": [ + "self-hosting/deployments/linux", + "self-hosting/deployments/kubernetes" + ] + }, + { + "group": "Configuration", + "pages": ["self-hosting/configuration/envars"] + } + ] + }, + { + "group": "Integrations", + "pages": [ + "integrations/overview" + ] + }, + { + "group": "Platforms", + "pages": [ + "integrations/platforms/docker", + "integrations/platforms/docker-compose" + ] + }, + { + "group": "Cloud", + "pages": [ + "integrations/cloud/heroku" + ] + }, + { + "group": "Frameworks", + "pages": [ + "integrations/frameworks/react", + "integrations/frameworks/vue", + "integrations/frameworks/express", + "integrations/frameworks/nextjs", + "integrations/frameworks/nestjs", + "integrations/frameworks/nuxt", + "integrations/frameworks/gatsby", + "integrations/frameworks/remix", + "integrations/frameworks/vite", + "integrations/frameworks/fiber", + "integrations/frameworks/django", + "integrations/frameworks/flask", + "integrations/frameworks/laravel", + "integrations/frameworks/rails" + ] + }, + { + "group": "Security", + "pages": [ + "security/overview", + "security/data-model", + "security/mechanics" + ] + }, + { + "group": "Contributing", + "pages": [ + "contributing/overview", + "contributing/code-of-conduct", + "contributing/developing", + "contributing/FAQ" + ] } ], "backgroundImage": "/images/background.png" diff --git a/docs/self-hosting/deployments/kubernetes.mdx b/docs/self-hosting/deployments/kubernetes.mdx new file mode 100644 index 000000000..8ed2c0e05 --- /dev/null +++ b/docs/self-hosting/deployments/kubernetes.mdx @@ -0,0 +1,54 @@ +--- +title: "Kubernetes" +description: "Deploy with Kubernetes" +--- + + +Self-host vs. Infisical Cloud + +Self-hosting Infisical means managing the service yourself, taking care of upgrades, scaling, security, etc. + +If you're less technical and looking for a hands-free experience with minimal overhead then we recommend Infisical Cloud. + + + +**Prerequisites** +- You have understanding of [Kubernetes](https://kubernetes.io/) +- You have understanding of [Helm package manager](https://helm.sh/) +- You have [kubectl](https://kubernetes.io/docs/reference/kubectl/kubectl/) installed and connected to your kubernetes cluster + + +#### 1. Fill our environment variables + +Before you can deploy the Helm chart, you must fill out the required environment variables. To do so, please either download or copy the +contents of [this file](https://raw.githubusercontent.com/Infisical/infisical/main/helm-charts/infisical/values.yaml) to a `.yaml` file. +_Refer to the available [environment variables](../../self-hosting/configuration/envars)_ + +Once you have a local copy of the values file, fill our the required environment variables and save the file. + + +#### 2. Install Infisical Helm repository + +```bash +helm repo add infisical-helm-charts 'https://dl.cloudsmith.io/public/infisical/helm-charts/helm/charts/' + +helm repo update +``` + +#### 3. Install the Helm chart + +By default, the helm chart will be installed on your default namespace. If you wish to install the Chart on a different namespace, you may specify +that by adding the `--namespace ` to your `helm install` command. + +```bash +## Installs to default namespace +helm install infisical-helm-charts/infisical --values +``` + + +If you have not filled out all of the required environment variables, you will see an error message prompting you to +do so. + + +4. Your Infisical installation is complete and should be running on the host name you specified in Ingress in `values.yaml`. +Note: Please allow an additional time (2 minutes) for the frontend pods to be fully ready. \ No newline at end of file diff --git a/docs/self-hosting/deployments/linux.mdx b/docs/self-hosting/deployments/linux.mdx index e3392e6c3..0282e46de 100644 --- a/docs/self-hosting/deployments/linux.mdx +++ b/docs/self-hosting/deployments/linux.mdx @@ -51,4 +51,4 @@ nano .env docker-compose -f docker-compose.yml up -d ``` -5. Your Infisical installation is complete and should be running on port 8080. Please note that the containers are not exposed to the internet and only bind to the localhost. It's up to you to configure a firewall, SSL certificates, and implement any additional security measures. +5. Your Infisical installation is complete and should be running on [http://localhost:8080](http://localhost:8080). Please note that the containers are not exposed to the internet and only bind to the localhost. It's up to you to configure a firewall, SSL certificates, and implement any additional security measures. diff --git a/docs/self-hosting/overview.mdx b/docs/self-hosting/overview.mdx index 8f2df5c37..b0729637c 100644 --- a/docs/self-hosting/overview.mdx +++ b/docs/self-hosting/overview.mdx @@ -9,17 +9,22 @@ Self-hosting Infisical means managing the service yourself, taking care of upgra If you're less technical and looking for a hands-free experience with minimal overhead then we recommend Infisical Cloud. -Infisical Cloud also comes with some extra features unavailabe in the self-hosted edition. You can find more information about Infisical Cloud's offering on the pricing page. +Infisical Cloud also comes with some extra features unavailable in the self-hosted edition. You can find more information about Infisical Cloud's offering on the pricing page. ## Deployment options -Infisical can be deployed on a Linux VM with docker-compose. We're rolling out more specific deployment options for DigitalOcean, AWS, GCP, and Azure soon. +Infisical can be deployed on a Linux VM with docker-compose and Kubernetes. We're rolling out more specific deployment options for DigitalOcean, AWS, GCP, and Azure soon. -Options: - -- [Linux VM](/self-hosting/deployments/linux) + + + Deploy to any Linux with Docker + + + Deploy to your Kubernetes cluster + + ## Telemetry diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc similarity index 65% rename from frontend/.eslintrc.json rename to frontend/.eslintrc index 0c5504b90..b562aaa1f 100644 --- a/frontend/.eslintrc.json +++ b/frontend/.eslintrc @@ -1,10 +1,21 @@ { - "extends": "next/core-web-vitals", - "plugins": [ - "simple-import-sort" + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "next", + "next/core-web-vitals" ], + "parser": "@typescript-eslint/parser", + "plugins": ["simple-import-sort", "@typescript-eslint"], "rules": { "react-hooks/exhaustive-deps": "off", + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-var-requires": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "simple-import-sort/exports": "warn", "simple-import-sort/imports": [ "warn", @@ -18,15 +29,9 @@ "^(assert|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|https|module|net|os|path|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|tty|url|util|vm|zlib|freelist|v8|process|async_hooks|http2|perf_hooks)(/.*|$)" ], // Packages `react` related packages - [ - "^react", - "^next", - "^@?\\w" - ], + ["^react", "^next", "^@?\\w"], // Internal packages. - [ - "^~(/.*|$)" - ], + ["^~(/.*|$)"], // Relative imports [ "^\\.\\.(?!/?$)", @@ -36,11 +41,9 @@ "^\\./?$" ], // Style imports. - [ - "^.+\\.?(css|scss)$" - ] + ["^.+\\.?(css|scss)$"] ] } ] } -} \ No newline at end of file +} diff --git a/frontend/README.md b/frontend/README.md index b33ff4335..d97bb4bef 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,20 +1,22 @@ This is the client repository for Infisical. -## Before you get started with development locally +## Before you get started with development locally + Please ensure you have Docker and Docker Compose installed for your OS. -### Steps to start server +### Steps to start server + - `CD` into the repo - run command `docker-compose -f docker-compose.dev.yml up --build --force-recreate` -- Vist localhost:3000 and the website should be live +- Visit localhost:3000 and the website should be live + +### Steps to shutdown this Docker compose -### Steps to shutdown this Docker compose - `CD` into this repo -- run command `docker-compose -f docker-compose-dev.yml down` +- run command `docker-compose -f docker-compose.dev.yml down` ### Notes -Any changes made to local files in the `/components`, `/pages`, `styles` will be hot reloaded. If would like like to watch for other files or folders live, please add them to the docker volume. + +Any changes made to local files in the `/components`, `/pages`, `/styles` will be hot reloaded. If would like like to watch for other files or folders live, please add them to the docker volume. You will also need to ensure that a .env.local file exists with all required environment variables - - diff --git a/frontend/components/basic/Error.js b/frontend/components/basic/Error.tsx similarity index 84% rename from frontend/components/basic/Error.js rename to frontend/components/basic/Error.tsx index 78a181016..79cc6e5f1 100644 --- a/frontend/components/basic/Error.js +++ b/frontend/components/basic/Error.tsx @@ -1,8 +1,8 @@ -import React, { useState } from "react"; +import React from "react"; import { faExclamationTriangle } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -export default function Error({ text }) { +export default function Error({ text }: { text: string }): JSX.Element { return (
void, + data: string[], + text: string, + buttonAction: () => void, + width: string, +} + /** * This is the component that we use for drop down lists. - * @param {*} param0 + * @param {object} obj + * @param {string} obj.selected - the item that is currently selected + * @param {function} obj.onChange - what happends if you select the item inside a list + * @param {string[]} obj.data - all the options available + * @param {string} obj.text - the text that shows us in front of the select option + * @param {function} obj.buttonAction - if there is a button at the bottom of the list, this is the action that happens when you click the button + * @param {string} obj.width - button width * @returns */ export default function ListBox({ @@ -21,10 +36,7 @@ export default function ListBox({ text, buttonAction, width, - workspaceMapping = [], -}) { - const router = useRouter(); - +} : ListBoxProps): JSX.Element { return (
@@ -61,7 +73,7 @@ export default function ListBox({ `my-0.5 relative cursor-default select-none py-2 pl-10 pr-4 rounded-md ${ selected ? "bg-white/10 text-gray-400 font-bold" : "" } ${ - active & !selected + active && !selected ? "bg-white/5 text-mineshaft-200 cursor-pointer" : "text-gray-400" } ` diff --git a/frontend/components/basic/buttons/Button.tsx b/frontend/components/basic/buttons/Button.tsx index c0db709c1..5fe2db803 100644 --- a/frontend/components/basic/buttons/Button.tsx +++ b/frontend/components/basic/buttons/Button.tsx @@ -1,35 +1,59 @@ import React from "react"; import Image from "next/image"; import { IconProp } from "@fortawesome/fontawesome-svg-core"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + FontAwesomeIcon, + FontAwesomeIconProps, +} from "@fortawesome/react-fontawesome"; -var classNames = require("classnames"); +const classNames = require("classnames"); type ButtonProps = { text: string; onButtonPressed: () => void; - loading: boolean; + loading?: boolean; color: string; size: string; - icon: IconProp; - active: boolean; - iconDisabled: string; - textDisabled: string; -} + icon?: IconProp; + active?: boolean; + iconDisabled?: string; + textDisabled?: string; +}; -export default function Button (props: ButtonProps): JSX.Element { +/** + * This is the main butto component in the app. + * @param {object} props + * @param {string} props.text - text inside the button + * @param {function} props.onButtonPressed - the action that happens when the button is clicked + * @param {boolean} props.loading - if a button is currently in the laoding state + * @param {string} props.color - button color + * @param {string} props.size - button size + * @param {FontAwesomeIconProps} props.icon - the icon inside the button + * @param {boolean} props.active - if the button is active or disabled + * @param {FontAwesomeIconProps} props.text - the icon inside the button when it is disabled + * @param {string} props.textDisable - text inside the button when it is disabled + * @returns + */ +export default function Button(props: ButtonProps): JSX.Element { // Check if the button show always be 'active' - then true; // or if it should switch between 'active' and 'disabled' - then give the status - const activityStatus = props.active || (props.text != "" && props.textDisabled == undefined) - - let styleButton = classNames( + const activityStatus = + props.active || (props.text != "" && props.textDisabled == undefined); + + const styleButton = classNames( "group m-auto md:m-0 inline-block rounded-md duration-200", // Setting background colors and hover modes - props.color == "mineshaft" && activityStatus && "bg-mineshaft-700 hover:bg-primary", + props.color == "mineshaft" && + activityStatus && + "bg-mineshaft-700 hover:bg-primary", props.color == "mineshaft" && !activityStatus && "bg-mineshaft", - (props.color == "primary" || !props.color) && activityStatus && "bg-primary hover:opacity-80", - (props.color == "primary" || !props.color) && !activityStatus && "bg-primary", + (props.color == "primary" || !props.color) && + activityStatus && + "bg-primary hover:opacity-80", + (props.color == "primary" || !props.color) && + !activityStatus && + "bg-primary", props.color == "red" && "bg-red", // Changing the opacity when active vs when not @@ -43,7 +67,7 @@ export default function Button (props: ButtonProps): JSX.Element { props.size == "icon-sm" && "h-9 w-9 flex items-center justify-center" ); - let styleMainDiv = classNames( + const styleMainDiv = classNames( "relative font-medium flex items-center", // Setting the text color for the text and icon @@ -55,11 +79,11 @@ export default function Button (props: ButtonProps): JSX.Element { props.size == "icon" && "flex items-center justify-center" ); - let textStyle = classNames( + const textStyle = classNames( "relative duration-200", // Show the loading sign if the loading indicator is on - props.loading == true ? "opacity-0" : "opacity-100", + props.loading ? "opacity-0" : "opacity-100", props.size == "md" && "text-sm", props.size == "lg" && "text-lg" ); @@ -86,7 +110,7 @@ export default function Button (props: ButtonProps): JSX.Element {
{props.icon && ( )} {(props.text || props.textDisabled) && ( -

{activityStatus ? props.text : props.textDisabled}

+

+ {activityStatus ? props.text : props.textDisabled} +

)}
diff --git a/frontend/components/basic/dialog/AddServiceTokenDialog.js b/frontend/components/basic/dialog/AddServiceTokenDialog.js index 33b07125d..59eecc29a 100644 --- a/frontend/components/basic/dialog/AddServiceTokenDialog.js +++ b/frontend/components/basic/dialog/AddServiceTokenDialog.js @@ -9,6 +9,7 @@ import nacl from "tweetnacl"; import addServiceToken from "~/pages/api/serviceToken/addServiceToken"; import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey"; +import { envMapping } from "../../../public/data/frequentConstants"; import { decryptAssymmetric, encryptAssymmetric, @@ -17,13 +18,6 @@ import Button from "../buttons/Button"; import InputField from "../InputField"; import ListBox from "../Listbox"; -const envMapping = { - Development: "dev", - Staging: "staging", - Production: "prod", - Testing: "test", -}; - const expiryMapping = { "1 day": 86400, "7 days": 604800, @@ -45,7 +39,7 @@ const AddServiceTokenDialog = ({ const { t } = useTranslate(); const generateServiceToken = async () => { - const latestFileKey = await getLatestFileKey(workspaceId); + const latestFileKey = await getLatestFileKey({ workspaceId }); const key = decryptAssymmetric({ ciphertext: latestFileKey.latestKey.encryptedKey, diff --git a/frontend/components/basic/layout.js b/frontend/components/basic/layout.js index aee5d5a99..c34888800 100644 --- a/frontend/components/basic/layout.js +++ b/frontend/components/basic/layout.js @@ -4,10 +4,11 @@ import Link from "next/link"; import { useRouter } from "next/router"; import useTranslation from "next-translate/useTranslation"; import { + faBookOpen, faGear, - faHouse, - faLink, + faKey, faMobile, + faPlug, faUser, } from "@fortawesome/free-solid-svg-icons"; import { faPlus } from "@fortawesome/free-solid-svg-icons"; @@ -19,6 +20,7 @@ import getOrganizationUsers from "~/pages/api/organization/GetOrgUsers"; import addUserToWorkspace from "~/pages/api/workspace/addUserToWorkspace"; import createWorkspace from "~/pages/api/workspace/createWorkspace"; import getWorkspaces from "~/pages/api/workspace/getWorkspaces"; +import uploadKeys from "~/pages/api/workspace/uploadKeys"; import NavBarDashboard from "../navigation/NavBarDashboard"; import { @@ -57,10 +59,10 @@ export default function Layout({ children }) { const workspaces = await getWorkspaces(); const currentWorkspaces = workspaces.map((workspace) => workspace.name); if (!currentWorkspaces.includes(workspaceName)) { - const newWorkspace = await createWorkspace( + const newWorkspace = await createWorkspace({ workspaceName, - localStorage.getItem("orgData.id") - ); + organizationId: localStorage.getItem("orgData.id"), + }); let newWorkspaceId; try { newWorkspaceId = newWorkspace._id; @@ -117,7 +119,7 @@ export default function Layout({ children }) { href: "/dashboard/" + workspaceMapping[workspaceSelected] + "?Development", title: t("nav:menu.secrets"), - emoji: , + emoji: , }, { href: "/users/" + workspaceMapping[workspaceSelected], @@ -127,7 +129,7 @@ export default function Layout({ children }) { { href: "/integrations/" + workspaceMapping[workspaceSelected], title: t("nav:menu.integrations"), - emoji: , + emoji: , }, { href: "/settings/project/" + workspaceMapping[workspaceSelected], @@ -160,7 +162,6 @@ export default function Layout({ children }) { 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" && @@ -217,66 +218,100 @@ export default function Layout({ children }) {
{ - let result = await getLatestFileKey(router.query.id); + let result = await getLatestFileKey({workspaceId: router.query.id}); const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY"); diff --git a/frontend/components/billing/Plan.js b/frontend/components/billing/Plan.js index f156cf76d..427469635 100644 --- a/frontend/components/billing/Plan.js +++ b/frontend/components/billing/Plan.js @@ -61,7 +61,7 @@ export default function Plan({ plan }) {
)} - +
{plan.buttonTextSecondary}
diff --git a/frontend/components/dashboard/DashboardInputField.js b/frontend/components/dashboard/DashboardInputField.js deleted file mode 100644 index 53733c38b..000000000 --- a/frontend/components/dashboard/DashboardInputField.js +++ /dev/null @@ -1,131 +0,0 @@ -import React, { Fragment } from "react"; -import { faCircle } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; - -import guidGenerator from "../utilities/randomId"; - -/** - * This function splits the input of a dashboard field into the parts that are inside and outside of ${...} - * @param {string} text - the value of the input in the Dashboard Input Field - * @returns - */ -const findReferences = (text) => { - var splitText = text.split("${"); - let textArray = [splitText[0]]; - for (var i = 1; i < splitText.length; i++) { - let insideBrackets = "${" + splitText[i].split("}")[0]; - if (splitText[i].includes("}")) { - insideBrackets += "}"; - } - textArray.push(insideBrackets); - textArray.push(splitText[i].split("}")[1]); - } - return textArray; -}; - -/** - * This component renders the input fields on the dashboard - * @param {object} obj - the order number of a keyPair - * @param {number} obj.index - the order number of a keyPair - * @param {function} obj.onChangeHandler - what happens when the input is modified - * @param {string} obj.type - whether the input field is for a Key Name or for a Key Value - * @param {string} obj.value - value of the InputField - * @param {boolean} obj.blurred - whether the input field should be blurred (behind the gray dots) or not; this can be turned on/off in the dashboard - * @returns - */ -const DashboardInputField = ({ - index, - onChangeHandler, - type, - value, - blurred, -}) => { - if (type === "varName") { - return ( -
-
- onChangeHandler(e.target.value, index)} - type={type} - value={value} - className="asolute z-10 peer font-mono ph-no-capture bg-bunker-800 rounded-md caret-white text-gray-400 text-md px-2 py-1.5 w-full min-w-16 outline-none focus:ring-4 focus:ring-primary/50 duration-200" - spellCheck="false" - /> -
-
- ); - } else if (type === "value") { - return ( -
-
- onChangeHandler(e.target.value, index)} - type={type} - value={value} - className={`${ - blurred - ? "text-transparent group-hover:text-transparent focus:text-transparent active:text-transparent" - : "" - } asolute z-10 peer font-mono ph-no-capture bg-transparent rounded-md caret-white text-transparent text-md px-2 py-1.5 w-full min-w-16 outline-none focus:ring-4 focus:ring-primary/50 duration-200`} - spellCheck="false" - /> -
- {findReferences(value).map((texts, id) => { - if (id % 2 == 0 || texts.length <= 2) { - return ( - - {texts} - - ); - } - return ( - - {texts.slice(0, 2)} - - {texts.slice(2, texts.length - 1)} - - {texts.slice(texts.length - 1, texts.length) == "}" ? ( - - {texts.slice(texts.length - 1, texts.length)}{" "} - - ) : ( - - {texts.slice(texts.length - 1, texts.length)}{" "} - - )} - - ); - })} -
- {blurred && ( -
-

- {value - .split("") - .slice(0, 42) - .map(() => ( - - ))} -
- )} -
-
- ); - } -}; - -export default React.memo(DashboardInputField); diff --git a/frontend/components/dashboard/DashboardInputField.tsx b/frontend/components/dashboard/DashboardInputField.tsx new file mode 100644 index 000000000..7a5b4a048 --- /dev/null +++ b/frontend/components/dashboard/DashboardInputField.tsx @@ -0,0 +1,156 @@ +import React, { SyntheticEvent, useRef } from "react"; +import { faCircle } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +import guidGenerator from "../utilities/randomId"; + +const REGEX = /([$]{.*?})/g; + +interface DashboardInputFieldProps { + index: number; + onChangeHandler: (value: string, index: number) => void; + value: string; + type: "varName" | "value"; + blurred: boolean; + duplicates: string[]; +} + +/** + * This component renders the input fields on the dashboard + * @param {object} obj - the order number of a keyPair + * @param {number} obj.index - the order number of a keyPair + * @param {function} obj.onChangeHandler - what happens when the input is modified + * @param {string} obj.type - whether the input field is for a Key Name or for a Key Value + * @param {string} obj.value - value of the InputField + * @param {boolean} obj.blurred - whether the input field should be blurred (behind the gray dots) or not; this can be turned on/off in the dashboard + * @param {string[]} obj.duplicates - list of all the duplicated key names on the dashboard + * @returns + */ + +const DashboardInputField = ({ + index, + onChangeHandler, + type, + value, + blurred, + duplicates, +}: DashboardInputFieldProps) => { + const ref = useRef(null); + const syncScroll = (e: SyntheticEvent) => { + if (ref.current == null) return; + + ref.current.scrollTop = e.currentTarget.scrollTop; + ref.current.scrollLeft = e.currentTarget.scrollLeft; + }; + + if (type === "varName") { + const startsWithNumber = !isNaN(Number(value.charAt(0))) && value != ""; + const hasDuplicates = duplicates?.includes(value); + const error = startsWithNumber || hasDuplicates; + + return ( +
+
+ + onChangeHandler(e.target.value.toUpperCase(), index) + } + type={type} + value={value} + className={`z-10 peer font-mono ph-no-capture bg-bunker-800 rounded-md caret-white text-gray-400 text-md px-2 py-1.5 w-full min-w-16 outline-none focus:ring-2 ${ + error ? "focus:ring-red/50" : "focus:ring-primary/50" + } duration-200`} + spellCheck="false" + /> +
+ {startsWithNumber && ( +

+ Should not start with a number +

+ )} + {hasDuplicates && !startsWithNumber && ( +

+ Secret names should be unique +

+ )} +
+ ); + } else if (type === "value") { + return ( +
+
+ onChangeHandler(e.target.value, index)} + onScroll={syncScroll} + className={`${ + blurred + ? "text-transparent group-hover:text-transparent focus:text-transparent active:text-transparent" + : "" + } z-10 peer font-mono ph-no-capture bg-transparent rounded-md caret-white text-transparent text-md px-2 py-1.5 w-full min-w-16 outline-none focus:ring-2 focus:ring-primary/50 duration-200 no-scrollbar no-scrollbar::-webkit-scrollbar`} + spellCheck="false" + /> +
+ {value.split(REGEX).map((word, id) => { + if (word.match(REGEX) !== null) { + return ( + + {word.slice(0, 2)} + + {word.slice(2, word.length - 1)} + + {word.slice(word.length - 1, word.length) == "}" ? ( + + {word.slice(word.length - 1, word.length)} + + ) : ( + + {word.slice(word.length - 1, word.length)} + + )} + + ); + } else { + return ( + + {word} + + ); + } + })} +
+ {blurred && ( +
+
+ {value.split("").map(() => ( + + ))} +
+
+ )} +
+
+ ); + } + + return <>Something Wrong; +}; + +export default React.memo(DashboardInputField); diff --git a/frontend/components/dashboard/DropZone.js b/frontend/components/dashboard/DropZone.tsx similarity index 69% rename from frontend/components/dashboard/DropZone.js rename to frontend/components/dashboard/DropZone.tsx index e3af663b1..4bc684cea 100644 --- a/frontend/components/dashboard/DropZone.js +++ b/frontend/components/dashboard/DropZone.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { type ChangeEvent, type DragEvent, useState } from "react"; import Image from "next/image"; import useTranslation from "next-translate/useTranslation"; import { faUpload } from "@fortawesome/free-solid-svg-icons"; @@ -9,29 +9,39 @@ import Error from "../basic/Error"; import parse from "../utilities/file"; import guidGenerator from "../utilities/randomId"; +interface DropZoneProps { + // TODO: change Data type from any + setData: (data: any) => void; + setErrorDragAndDrop: (hasError: boolean) => void; + createNewFile: () => void; + errorDragAndDrop: boolean; + setButtonReady: (isReady: boolean) => void; + keysExist: boolean; + numCurrentRows: number; +} + const DropZone = ({ setData, setErrorDragAndDrop, createNewFile, errorDragAndDrop, - addPresetRow, setButtonReady, keysExist, numCurrentRows, -}) => { +}: DropZoneProps) => { const { t } = useTranslation(); - const handleDragEnter = (e) => { + const handleDragEnter = (e: DragEvent) => { e.preventDefault(); e.stopPropagation(); }; - const handleDragLeave = (e) => { + const handleDragLeave = (e: DragEvent) => { e.preventDefault(); e.stopPropagation(); }; - const handleDragOver = (e) => { + const handleDragOver = (e: DragEvent) => { e.preventDefault(); e.stopPropagation(); @@ -42,22 +52,25 @@ const DropZone = ({ const [loading, setLoading] = useState(false); // This function function immediately parses the file after it is dropped - const handleDrop = async (e) => { + const handleDrop = async (e: DragEvent) => { setLoading(true); setTimeout(() => setLoading(false), 5000); e.preventDefault(); e.stopPropagation(); e.dataTransfer.dropEffect = "copy"; - var file = e.dataTransfer.files[0], - reader = new FileReader(); - reader.onload = function (event) { - const keyPairs = parse(event.target.result); + const file = e.dataTransfer.files[0]; + const reader = new FileReader(); + + reader.onload = (event) => { + 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], + keyPairs[key as keyof typeof keyPairs], "shared", ]); setData(newData); @@ -75,23 +88,28 @@ const DropZone = ({ }; // This function is used when the user manually selects a file from the in-browser dircetory (not drag and drop) - const handleFileSelect = (e) => { + const handleFileSelect = (e: ChangeEvent) => { setLoading(true); setTimeout(() => setLoading(false), 5000); - var file = e.target.files[0], - reader = new FileReader(); - reader.onload = function (event) { - const newData = event.target.result - .split("\n") - .map((line, index) => [ - guidGenerator(), - numCurrentRows + index, - line.split("=")[0], - line.split("=").slice(1, line.split("=").length).join("="), - "shared", - ]); - setData(newData); - setButtonReady(true); + if (e.currentTarget.files === null) return; + const file = e.currentTarget.files[0]; + const reader = new FileReader(); + reader.onload = (event) => { + if (event.target === null || event.target.result === null) return; + const { result } = event.target; + 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", + ]); + setData(newData); + setButtonReady(true); + } }; reader.readAsText(file); }; @@ -108,17 +126,17 @@ const DropZone = ({ ) : keysExist ? (
handleDragEnter(e)} - onDragOver={(e) => handleDragOver(e)} - onDragLeave={(e) => handleDragLeave(e)} - onDrop={(e) => handleDrop(e)} + onDragEnter={handleDragEnter} + onDragOver={handleDragOver} + onDragLeave={handleDragLeave} + onDrop={handleDrop} > handleFileSelect(e)} + onChange={handleFileSelect} /> {errorDragAndDrop ? (
@@ -143,10 +161,10 @@ const DropZone = ({ ) : (
handleDragEnter(e)} - onDragOver={(e) => handleDragOver(e)} - onDragLeave={(e) => handleDragLeave(e)} - onDrop={(e) => handleDrop(e)} + onDragEnter={handleDragEnter} + onDragOver={handleDragOver} + onDragLeave={handleDragLeave} + onDrop={handleDrop} >

{t("common:drop-zone")}

@@ -155,7 +173,7 @@ const DropZone = ({ type="file" className="opacity-0 absolute w-full h-full" accept=".txt,.env" - onChange={(e) => handleFileSelect(e)} + onChange={handleFileSelect} />
diff --git a/frontend/components/navigation/NavBarDashboard.js b/frontend/components/navigation/NavBarDashboard.tsx similarity index 91% rename from frontend/components/navigation/NavBarDashboard.js rename to frontend/components/navigation/NavBarDashboard.tsx index 9644c861e..159a69daa 100644 --- a/frontend/components/navigation/NavBarDashboard.js +++ b/frontend/components/navigation/NavBarDashboard.tsx @@ -21,6 +21,7 @@ import getOrganization from "../../pages/api/organization/GetOrg"; import getOrganizations from "../../pages/api/organization/getOrgs"; import getUser from "../../pages/api/user/getUser"; import guidGenerator from "../utilities/randomId"; +import logout from "~/pages/api/auth/Logout"; /** * @param {(key: string) => string} t */ @@ -47,31 +48,48 @@ const supportOptions = (t) => [ ], ]; -export default function Navbar({ onButtonPressed }) { +export interface ICurrentOrg { + name: string; +} + +export interface IUser { + firstName: string; + lastName: string; + email: string; +} + +/** + * This is the navigation bar in the main app. + * It has two main components: support options and user menu (inlcudes billing, logout, org/user settings) + * @returns NavBar + */ +export default function Navbar() { const router = useRouter(); - const [user, setUser] = useState({}); + const [user, setUser] = useState(); const [orgs, setOrgs] = useState([]); - const [currentOrg, setCurrentOrg] = useState([]); + const [currentOrg, setCurrentOrg] = useState(); const { t } = useTranslation(""); const supportOptionsList = useMemo(() => supportOptions(t), [t]); - useEffect(async () => { - const userData = await getUser(); - setUser(userData); - const orgsData = await getOrganizations(); - setOrgs(orgsData); - const currentOrg = await getOrganization({ - orgId: localStorage.getItem("orgData.id"), - }); - setCurrentOrg(currentOrg); + useEffect(() => { + (async () => { + const userData = await getUser(); + setUser(userData); + const orgsData = await getOrganizations(); + setOrgs(orgsData); + const currentOrg = await getOrganization({ + orgId: String(localStorage.getItem("orgData.id")), + }); + setCurrentOrg(currentOrg); + })(); }, []); const closeApp = async () => { console.log("Logging out..."); await logout(); - router.push("/"); + router.push("/login"); }; return ( @@ -110,8 +128,8 @@ export default function Navbar({ onButtonPressed }) {
@@ -241,9 +259,10 @@ export default function Navbar({ onButtonPressed }) {
{orgs .filter( - (org) => org._id != localStorage.getItem("orgData.id") + (org: { _id: string }) => + org._id != localStorage.getItem("orgData.id") ) - .map((org) => ( + .map((org: { _id: string; name: string }) => (
{ diff --git a/frontend/components/navigation/NavHeader.js b/frontend/components/navigation/NavHeader.tsx similarity index 67% rename from frontend/components/navigation/NavHeader.js rename to frontend/components/navigation/NavHeader.tsx index 3f65a5aa6..3d64c2eda 100644 --- a/frontend/components/navigation/NavHeader.js +++ b/frontend/components/navigation/NavHeader.tsx @@ -1,29 +1,37 @@ import React, { useEffect, useState } from "react"; import { useRouter } from "next/router"; -import { faCcMastercard, faCcVisa } from "@fortawesome/free-brands-svg-icons"; import { faAngleRight, - faQuestionCircle, } from "@fortawesome/free-solid-svg-icons"; -import { faCircle } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import getOrganization from "~/pages/api/organization/GetOrg"; -import getWorkspaceInfo from "~/pages/api/workspace/getWorkspaceInfo"; +import getProjectInfo from "~/pages/api/workspace/getProjectInfo"; -export default function NavHeader({ pageName, isProjectRelated }) { +/** + * This is the component at the top of almost every page. + * It shows how to navigate to a certain page. + * It future these links should also be clickable and hoverable + * @param obj + * @param obj.pageName - Name of the page + * @param obj.isProjectRelated - whether this page is related to project or now (determine if it's 2 or 3 navigation steps) + * @returns + */ +export default function NavHeader({ pageName, isProjectRelated } : { pageName: string; isProjectRelated: boolean; }): JSX.Element { const [orgName, setOrgName] = useState(""); const [workspaceName, setWorkspaceName] = useState(""); const router = useRouter(); useEffect(() => { (async () => { - let org = await getOrganization({ - orgId: localStorage.getItem("orgData.id"), + const orgId = localStorage.getItem("orgData.id") + const org = await getOrganization({ + orgId: orgId ? orgId : "", }); setOrgName(org.name); - let workspace = await getWorkspaceInfo({ - workspaceId: router.query.id, + + const workspace = await getProjectInfo({ + projectId: String(router.query.id), }); setWorkspaceName(workspace.name); })(); diff --git a/frontend/components/utilities/SecurityClient.js b/frontend/components/utilities/SecurityClient.js index 645ecbb67..f8be6a0e1 100644 --- a/frontend/components/utilities/SecurityClient.js +++ b/frontend/components/utilities/SecurityClient.js @@ -1,8 +1,6 @@ -import { PATH } from "~/const"; import token from "~/pages/api/auth/Token"; export default class SecurityClient { - static authOrigins = [PATH]; static #token = ""; contructor() {} @@ -13,13 +11,12 @@ export default class SecurityClient { static async fetchCall(resource, options) { let req = new Request(resource, options); - const destOrigin = new URL(req.url).origin; if (this.#token == "") { this.setToken(await token()); } - if (this.#token && this.authOrigins.includes(destOrigin)) { + if (this.#token) { req.headers.set("Authorization", "Bearer " + this.#token); return fetch(req); } diff --git a/frontend/components/utilities/attemptLogin.js b/frontend/components/utilities/attemptLogin.js index 4e74a40ab..4c6852625 100644 --- a/frontend/components/utilities/attemptLogin.js +++ b/frontend/components/utilities/attemptLogin.js @@ -43,7 +43,7 @@ const attemptLogin = async ( let serverPublicKey, salt; try { - const res = await login1(email, clientPublicKey); + let res = await login1(email, clientPublicKey); res = await res.json(); serverPublicKey = res.serverPublicKey; salt = res.salt; @@ -134,10 +134,18 @@ const attemptLogin = async ( // If user is logging in for the first time, add the example keys if (isSignUp) { - await pushKeys( - { + await pushKeys({ + obj: { DATABASE_URL: [ - "mongodb+srv://this_is:an_example@mongodb.net", + "mongodb+srv://${DB_USERNAME}:${DB_PASSWORD}@mongodb.net", + "personal", + ], + DB_USERNAME: [ + "user1234", + "personal", + ], + DB_PASSWORD: [ + "ah8jak3hk8dhiu4dw7whxwe1l", "personal", ], TWILIO_AUTH_TOKEN: [ @@ -147,9 +155,9 @@ const attemptLogin = async ( WEBSITE_URL: ["http://localhost:3000", "shared"], STRIPE_SECRET_KEY: ["sk_test_7348oyho4hfq398HIUOH78", "shared"], }, - projectToLogin, - "Development" - ); + workspaceId: projectToLogin, + env: "Development" + }); } try { if (email) { diff --git a/frontend/components/utilities/cryptography/changePassword.js b/frontend/components/utilities/cryptography/changePassword.js index 8bfaad674..de0fd9c3a 100644 --- a/frontend/components/utilities/cryptography/changePassword.js +++ b/frontend/components/utilities/cryptography/changePassword.js @@ -108,11 +108,7 @@ const changePassword = async ( } ); } catch (error) { - console.log( - "Something went wrong during changing the password", - slat, - serverPublicKey - ); + console.log("Something went wrong during changing the password"); } return true; }; diff --git a/frontend/components/utilities/cryptography/crypto.js b/frontend/components/utilities/cryptography/crypto.ts similarity index 81% rename from frontend/components/utilities/cryptography/crypto.js rename to frontend/components/utilities/cryptography/crypto.ts index 842911174..a50ee6a0a 100644 --- a/frontend/components/utilities/cryptography/crypto.js +++ b/frontend/components/utilities/cryptography/crypto.ts @@ -2,6 +2,12 @@ const nacl = require("tweetnacl"); nacl.util = require("tweetnacl-util"); const aes = require("./aes-256-gcm"); +type encryptAsymmetricProps = { + plaintext: string; + publicKey: string; + privateKey: string; +} + /** * Return assymmetrically encrypted [plaintext] using [publicKey] where * [publicKey] likely belongs to the recipient. @@ -13,7 +19,7 @@ const aes = require("./aes-256-gcm"); * @returns {String} ciphertext - base64-encoded ciphertext * @returns {String} nonce - base64-encoded nonce */ -const encryptAssymmetric = ({ plaintext, publicKey, privateKey }) => { +const encryptAssymmetric = ({ plaintext, publicKey, privateKey }: encryptAsymmetricProps): object => { const nonce = nacl.randomBytes(24); const ciphertext = nacl.box( nacl.util.decodeUTF8(plaintext), @@ -28,6 +34,13 @@ const encryptAssymmetric = ({ plaintext, publicKey, privateKey }) => { }; }; +type decryptAsymmetricProps = { + ciphertext: string; + nonce: string; + publicKey: string; + privateKey: string; +} + /** * Return assymmetrically decrypted [ciphertext] using [privateKey] where * [privateKey] likely belongs to the recipient. @@ -38,7 +51,7 @@ const encryptAssymmetric = ({ plaintext, publicKey, privateKey }) => { * @param {String} obj.privateKey - base64-encoded private key of the receiver (current user) * @param {String} plaintext - UTF8 plaintext */ -const decryptAssymmetric = ({ ciphertext, nonce, publicKey, privateKey }) => { +const decryptAssymmetric = ({ ciphertext, nonce, publicKey, privateKey }: decryptAsymmetricProps): string => { const plaintext = nacl.box.open( nacl.util.decodeBase64(ciphertext), nacl.util.decodeBase64(nonce), @@ -49,13 +62,18 @@ const decryptAssymmetric = ({ ciphertext, nonce, publicKey, privateKey }) => { return nacl.util.encodeUTF8(plaintext); }; +type encryptSymmetricProps = { + plaintext: string; + key: string; +} + /** * Return symmetrically encrypted [plaintext] using [key]. * @param {Object} obj * @param {String} obj.plaintext - plaintext to encrypt * @param {String} obj.key - 16-byte hex key */ -const encryptSymmetric = ({ plaintext, key }) => { +const encryptSymmetric = ({ plaintext, key }: encryptSymmetricProps): object => { let ciphertext, iv, tag; try { const obj = aes.encrypt(plaintext, key); @@ -75,6 +93,13 @@ const encryptSymmetric = ({ plaintext, key }) => { }; }; +type decryptSymmetricProps = { + ciphertext: string; + iv: string; + tag: string; + key: string; +} + /** * Return symmetrically decypted [ciphertext] using [iv], [tag], * and [key]. @@ -85,7 +110,7 @@ const encryptSymmetric = ({ plaintext, key }) => { * @param {String} obj.key - 32-byte hex key * */ -const decryptSymmetric = ({ ciphertext, iv, tag, key }) => { +const decryptSymmetric = ({ ciphertext, iv, tag, key }: decryptSymmetricProps): string => { let plaintext; try { plaintext = aes.decrypt(ciphertext, iv, tag, key); @@ -97,9 +122,9 @@ const decryptSymmetric = ({ ciphertext, iv, tag, key }) => { return plaintext; }; -module.exports = { - encryptAssymmetric, +export { decryptAssymmetric, - encryptSymmetric, decryptSymmetric, + encryptAssymmetric, + encryptSymmetric, }; diff --git a/frontend/components/utilities/secrets/getSecretsForProject.js b/frontend/components/utilities/secrets/getSecretsForProject.js index ba502ccfd..7bdac922f 100644 --- a/frontend/components/utilities/secrets/getSecretsForProject.js +++ b/frontend/components/utilities/secrets/getSecretsForProject.js @@ -1,5 +1,6 @@ import getSecrets from "~/pages/api/files/GetSecrets"; +import { envMapping } from "../../../public/data/frequentConstants"; import guidGenerator from "../randomId"; const { @@ -9,13 +10,6 @@ const { const nacl = require("tweetnacl"); nacl.util = require("tweetnacl-util"); -const envMapping = { - Development: "dev", - Staging: "staging", - Production: "prod", - Testing: "test", -}; - const getSecretsForProject = async ({ env, setFileState, diff --git a/frontend/components/utilities/secrets/pushKeys.js b/frontend/components/utilities/secrets/pushKeys.ts similarity index 67% rename from frontend/components/utilities/secrets/pushKeys.js rename to frontend/components/utilities/secrets/pushKeys.ts index 97ec31c06..0d570f273 100644 --- a/frontend/components/utilities/secrets/pushKeys.js +++ b/frontend/components/utilities/secrets/pushKeys.ts @@ -2,29 +2,35 @@ import uploadSecrets from "~/pages/api/files/UploadSecrets"; import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey"; import getWorkspaceKeys from "~/pages/api/workspace/getWorkspaceKeys"; +import { envMapping } from "../../../public/data/frequentConstants"; + const crypto = require("crypto"); const { decryptAssymmetric, - decryptSymmetric, encryptSymmetric, encryptAssymmetric, } = require("../cryptography/crypto"); const nacl = require("tweetnacl"); nacl.util = require("tweetnacl-util"); -const envMapping = { - Development: "dev", - Staging: "staging", - Production: "prod", - Testing: "test", -}; +export interface IK { + publicKey: string; + userId: string; +} -const pushKeys = async (obj, workspaceId, env) => { - let sharedKey = await getLatestFileKey(workspaceId); +/** + * This function pushes the keys to the database after decrypting them end-to-end + * @param {object} obj + * @param {object} obj.obj - object with all the key pairs + * @param {object} obj.workspaceId - the id of a project to which a user is pushing + * @param {object} obj.env - which environment a user is pushing to + */ +const pushKeys = async({ obj, workspaceId, env }: { obj: object; workspaceId: string; env: string; }) => { + const sharedKey = await getLatestFileKey({ workspaceId }); const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY"); - let randomBytes; + let randomBytes: string; if (Object.keys(sharedKey).length > 0) { // case: a (shared) key exists for the workspace randomBytes = decryptAssymmetric({ @@ -55,11 +61,11 @@ const pushKeys = async (obj, workspaceId, env) => { iv: ivValue, tag: tagValue, } = encryptSymmetric({ - plaintext: obj[key][0], + plaintext: obj[key as keyof typeof obj][0], key: randomBytes, }); - const visibility = obj[key][1] != null ? obj[key][1] : "personal"; + const visibility = obj[key as keyof typeof obj][1] != null ? obj[key as keyof typeof obj][1] : "personal"; return { ciphertextKey, @@ -69,7 +75,7 @@ const pushKeys = async (obj, workspaceId, env) => { ciphertextValue, ivValue, tagValue, - hashValue: crypto.createHash("sha256").update(obj[key][0]).digest("hex"), + hashValue: crypto.createHash("sha256").update(obj[key as keyof typeof obj][0]).digest("hex"), type: visibility, }; }); @@ -80,7 +86,7 @@ const pushKeys = async (obj, workspaceId, env) => { }); // assymmetrically encrypt key with each receiver public keys - const keys = publicKeys.map((k) => { + const keys = publicKeys.map((k: IK) => { const { ciphertext, nonce } = encryptAssymmetric({ plaintext: randomBytes, publicKey: k.publicKey, @@ -99,7 +105,7 @@ const pushKeys = async (obj, workspaceId, env) => { workspaceId, secrets, keys, - environment: envMapping[env], + environment: envMapping[env as keyof typeof envMapping], }); }; diff --git a/frontend/const.js b/frontend/const.js index 01f7dcf7e..f3b285e43 100644 --- a/frontend/const.js +++ b/frontend/const.js @@ -1,5 +1,3 @@ -export const PATH = process.env.NEXT_PUBLIC_WEBSITE_URL; - export const publicPaths = [ `/`, // `/integrations`, diff --git a/frontend/locales/en-US.json b/frontend/locales/en-US.json index 4dc57a519..defb49856 100644 --- a/frontend/locales/en-US.json +++ b/frontend/locales/en-US.json @@ -209,7 +209,7 @@ "description": "Manage your integrations of Infisical with third-party services.", "no-integrations1": "You don't have any integrations set up yet. When you do, they will appear here.", "no-integrations2": "To start, click on any of the options below. It takes 5 clicks to set up.", - "available": "Available Integrations", + "available": "Platform & Cloud Integrations", "available-text1": "Click on the itegration you want to connect. This will let your environment variables flow automatically into selected third-party services.", "available-text2": " Note: during an integration with Heroku, for security reasons, it is impossible to maintain end-to-end encryption. In theory, this lets Infisical decrypt yor environment variables. In practice, we can assure you that this will never be done, and it allows us to protect your secrets from bad actors online. The core Infisical service will always stay end-to-end encrypted. With any questions, reach out support@infisical.com." } diff --git a/frontend/next.config.js b/frontend/next.config.js index 429c91c49..905aa8c8a 100644 --- a/frontend/next.config.js +++ b/frontend/next.config.js @@ -2,16 +2,12 @@ const nextTranslate = require("next-translate"); 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://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; + child-src 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' 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; 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; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ad2ecb75e..38dd5afd0 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -28,6 +28,7 @@ "jsrp": "^0.2.4", "markdown-it": "^13.0.1", "next": "^12.2.5", + "next-translate": "^1.6.0", "posthog-js": "^1.34.0", "query-string": "^7.1.1", "react": "^17.0.2", @@ -50,9 +51,12 @@ "devDependencies": { "@tailwindcss/typography": "^0.5.4", "@types/node": "18.11.9", + "@typescript-eslint/eslint-plugin": "^5.45.0", + "@typescript-eslint/parser": "^5.45.0", "autoprefixer": "^10.4.7", - "eslint": "^8.28.0", + "eslint": "^8.29.0", "eslint-config-next": "^13.0.5", + "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-simple-import-sort": "^8.0.0", "postcss": "^8.4.14", "prettier": "2.7.1", @@ -60,32 +64,6 @@ "typescript": "^4.9.3" } }, - "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "peer": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@ampproject/remapping/node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", - "peer": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -97,77 +75,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/compat-data": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.1.tgz", - "integrity": "sha512-72a9ghR0gnESIa7jBN53U32FOVCEoztyIlKaNoU05zRhEecduGK9L9c3ww7Mp06JiR+0ls0GBPFJQwwtjn9ksg==", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.1.tgz", - "integrity": "sha512-1H8VgqXme4UXCRv7/Wa1bq7RVymKOzC7znjyFM8KiEzwFqcKUKYNoQef4GhdklgNvoBXyW4gYhuBNCM5o1zImw==", - "peer": true, - "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.0", - "@babel/helper-compilation-targets": "^7.19.1", - "@babel/helper-module-transforms": "^7.19.0", - "@babel/helpers": "^7.19.0", - "@babel/parser": "^7.19.1", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.1", - "@babel/types": "^7.19.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "peer": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@babel/core/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "peer": true - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "peer": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/generator": { "version": "7.19.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.0.tgz", @@ -192,33 +99,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.1.tgz", - "integrity": "sha512-LlLkkqhCMyz2lkQPvJNdIYU7O5YjWRgC2R4omjCTpZd8u8KMQzZvX4qce+/BluN1rcQiV7BoGUpmQ0LeHerbhg==", - "peer": true, - "dependencies": { - "@babel/compat-data": "^7.19.1", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "peer": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/helper-environment-visitor": { "version": "7.18.9", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", @@ -261,25 +141,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", - "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", - "peer": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-plugin-utils": { "version": "7.19.0", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", @@ -288,18 +149,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", - "peer": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-split-export-declaration": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", @@ -327,29 +176,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.0.tgz", - "integrity": "sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==", - "peer": true, - "dependencies": { - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/highlight": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", @@ -1264,6 +1090,12 @@ "hoist-non-react-statics": "^3.3.0" } }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -1336,6 +1168,12 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" }, + "node_modules/@types/semver": { + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "dev": true + }, "node_modules/@types/unist": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", @@ -1351,15 +1189,71 @@ "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==" }, - "node_modules/@typescript-eslint/parser": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.44.0.tgz", - "integrity": "sha512-H7LCqbZnKqkkgQHaKLGC6KUjt3pjJDx8ETDqmwncyb6PuoigYajyAwBGz08VU/l86dZWZgI4zm5k2VaKqayYyA==", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.0.tgz", + "integrity": "sha512-CXXHNlf0oL+Yg021cxgOdMHNTXD17rHkq7iW6RFHoybdFgQBjU3yIXhhcPpGwr1CjZlo6ET8C6tzX5juQoXeGA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.44.0", - "@typescript-eslint/types": "5.44.0", - "@typescript-eslint/typescript-estree": "5.44.0", + "@typescript-eslint/scope-manager": "5.45.0", + "@typescript-eslint/type-utils": "5.45.0", + "@typescript-eslint/utils": "5.45.0", + "debug": "^4.3.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.45.0.tgz", + "integrity": "sha512-brvs/WSM4fKUmF5Ot/gEve6qYiCMjm6w4HkHPfS6ZNmxTS0m0iNN4yOChImaCkqc1hRwFGqUyanMXuGal6oyyQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.45.0", + "@typescript-eslint/types": "5.45.0", + "@typescript-eslint/typescript-estree": "5.45.0", "debug": "^4.3.4" }, "engines": { @@ -1402,13 +1296,13 @@ "dev": true }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.44.0.tgz", - "integrity": "sha512-2pKml57KusI0LAhgLKae9kwWeITZ7IsZs77YxyNyIVOwQ1kToyXRaJLl+uDEXzMN5hnobKUOo2gKntK9H1YL8g==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.45.0.tgz", + "integrity": "sha512-noDMjr87Arp/PuVrtvN3dXiJstQR1+XlQ4R1EvzG+NMgXi8CuMCXpb8JqNtFHKceVSQ985BZhfRdowJzbv4yKw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.44.0", - "@typescript-eslint/visitor-keys": "5.44.0" + "@typescript-eslint/types": "5.45.0", + "@typescript-eslint/visitor-keys": "5.45.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1418,10 +1312,60 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.45.0.tgz", + "integrity": "sha512-DY7BXVFSIGRGFZ574hTEyLPRiQIvI/9oGcN8t1A7f6zIs6ftbrU0nhyV26ZW//6f85avkwrLag424n+fkuoJ1Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.45.0", + "@typescript-eslint/utils": "5.45.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "node_modules/@typescript-eslint/types": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.44.0.tgz", - "integrity": "sha512-Tp+zDnHmGk4qKR1l+Y1rBvpjpm5tGXX339eAlRBDg+kgZkz9Bw+pqi4dyseOZMsGuSH69fYfPJCBKBrbPCxYFQ==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.45.0.tgz", + "integrity": "sha512-QQij+u/vgskA66azc9dCmx+rev79PzX8uDHpsqSjEFtfF2gBUTRCpvYMh2gw2ghkJabNkPlSUCimsyBEQZd1DA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1432,13 +1376,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.44.0.tgz", - "integrity": "sha512-M6Jr+RM7M5zeRj2maSfsZK2660HKAJawv4Ud0xT+yauyvgrsHu276VtXlKDFnEmhG+nVEd0fYZNXGoAgxwDWJw==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.0.tgz", + "integrity": "sha512-maRhLGSzqUpFcZgXxg1qc/+H0bT36lHK4APhp0AEUVrpSwXiRAomm/JGjSG+kNUio5kAa3uekCYu/47cnGn5EQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.44.0", - "@typescript-eslint/visitor-keys": "5.44.0", + "@typescript-eslint/types": "5.45.0", + "@typescript-eslint/visitor-keys": "5.45.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1481,13 +1425,61 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.44.0.tgz", - "integrity": "sha512-a48tLG8/4m62gPFbJ27FxwCOqPKxsb8KC3HkmYoq2As/4YyjQl1jDbRr1s63+g4FS/iIehjmN3L5UjmKva1HzQ==", + "node_modules/@typescript-eslint/utils": { + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.45.0.tgz", + "integrity": "sha512-OUg2JvsVI1oIee/SwiejTot2OxwU8a7UfTFMOdlhD2y+Hl6memUSL4s98bpUTo8EpVEr0lmwlU7JSu/p2QpSvA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.44.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.45.0", + "@typescript-eslint/types": "5.45.0", + "@typescript-eslint/typescript-estree": "5.45.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.0.tgz", + "integrity": "sha512-jc6Eccbn2RtQPr1s7th6jJWQHBHI6GBVQkCHoJFQ5UreaKm59Vxw+ynQUPPY2u2Amquc+7tmEoC2G52ApsGNNg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.45.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -1890,6 +1882,7 @@ "version": "4.21.3", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -2479,7 +2472,8 @@ "node_modules/electron-to-chromium": { "version": "1.4.206", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.206.tgz", - "integrity": "sha512-h+Fadt1gIaQ06JaIiyqPsBjJ08fV5Q7md+V8bUvQW/9OvXfL2LRICTz2EcnnCP7QzrFTS6/27MRV6Bl9Yn97zA==" + "integrity": "sha512-h+Fadt1gIaQ06JaIiyqPsBjJ08fV5Q7md+V8bUvQW/9OvXfL2LRICTz2EcnnCP7QzrFTS6/27MRV6Bl9Yn97zA==", + "dev": true }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -2601,6 +2595,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, "engines": { "node": ">=6" } @@ -2614,9 +2609,9 @@ } }, "node_modules/eslint": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.28.0.tgz", - "integrity": "sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==", + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", + "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.3.3", @@ -3501,15 +3496,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/get-intrinsic": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", @@ -4240,18 +4226,6 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", - "peer": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/jsonp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/jsonp/-/jsonp-0.2.1.tgz", @@ -5091,6 +5065,12 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, "node_modules/next": { "version": "12.3.1", "resolved": "https://registry.npmjs.org/next/-/next-12.3.1.tgz", @@ -5143,10 +5123,20 @@ } } }, + "node_modules/next-translate": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/next-translate/-/next-translate-1.6.0.tgz", + "integrity": "sha512-rmRBYOwPHvokN+5uH3O/SZqfRUVORLkgt/097mSaq3+CWcEtXEjkVrfBZpYwZ8W3og4QVd+uphYX//qegnIbRg==", + "peerDependencies": { + "next": ">= 10.0.0", + "react": ">= 16.8.0" + } + }, "node_modules/node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true }, "node_modules/normalize-path": { "version": "3.0.0", @@ -7022,6 +7012,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "dev": true, "funding": [ { "type": "opencollective", @@ -7227,28 +7218,6 @@ } }, "dependencies": { - "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "peer": true, - "requires": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", - "peer": true, - "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - } - } - }, "@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -7257,58 +7226,6 @@ "@babel/highlight": "^7.18.6" } }, - "@babel/compat-data": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.1.tgz", - "integrity": "sha512-72a9ghR0gnESIa7jBN53U32FOVCEoztyIlKaNoU05zRhEecduGK9L9c3ww7Mp06JiR+0ls0GBPFJQwwtjn9ksg==", - "peer": true - }, - "@babel/core": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.1.tgz", - "integrity": "sha512-1H8VgqXme4UXCRv7/Wa1bq7RVymKOzC7znjyFM8KiEzwFqcKUKYNoQef4GhdklgNvoBXyW4gYhuBNCM5o1zImw==", - "peer": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.0", - "@babel/helper-compilation-targets": "^7.19.1", - "@babel/helper-module-transforms": "^7.19.0", - "@babel/helpers": "^7.19.0", - "@babel/parser": "^7.19.1", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.1", - "@babel/types": "^7.19.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "peer": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "peer": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "peer": true - } - } - }, "@babel/generator": { "version": "7.19.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.0.tgz", @@ -7327,26 +7244,6 @@ "@babel/types": "^7.18.6" } }, - "@babel/helper-compilation-targets": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.1.tgz", - "integrity": "sha512-LlLkkqhCMyz2lkQPvJNdIYU7O5YjWRgC2R4omjCTpZd8u8KMQzZvX4qce+/BluN1rcQiV7BoGUpmQ0LeHerbhg==", - "peer": true, - "requires": { - "@babel/compat-data": "^7.19.1", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "peer": true - } - } - }, "@babel/helper-environment-visitor": { "version": "7.18.9", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", @@ -7377,36 +7274,11 @@ "@babel/types": "^7.18.6" } }, - "@babel/helper-module-transforms": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", - "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", - "peer": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" - } - }, "@babel/helper-plugin-utils": { "version": "7.19.0", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==" }, - "@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", - "peer": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, "@babel/helper-split-export-declaration": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", @@ -7425,23 +7297,6 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==" }, - "@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", - "peer": true - }, - "@babel/helpers": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.0.tgz", - "integrity": "sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==", - "peer": true, - "requires": { - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" - } - }, "@babel/highlight": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", @@ -8070,6 +7925,12 @@ "hoist-non-react-statics": "^3.3.0" } }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -8142,6 +8003,12 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" }, + "@types/semver": { + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "dev": true + }, "@types/unist": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", @@ -8157,15 +8024,49 @@ "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==" }, - "@typescript-eslint/parser": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.44.0.tgz", - "integrity": "sha512-H7LCqbZnKqkkgQHaKLGC6KUjt3pjJDx8ETDqmwncyb6PuoigYajyAwBGz08VU/l86dZWZgI4zm5k2VaKqayYyA==", + "@typescript-eslint/eslint-plugin": { + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.0.tgz", + "integrity": "sha512-CXXHNlf0oL+Yg021cxgOdMHNTXD17rHkq7iW6RFHoybdFgQBjU3yIXhhcPpGwr1CjZlo6ET8C6tzX5juQoXeGA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.44.0", - "@typescript-eslint/types": "5.44.0", - "@typescript-eslint/typescript-estree": "5.44.0", + "@typescript-eslint/scope-manager": "5.45.0", + "@typescript-eslint/type-utils": "5.45.0", + "@typescript-eslint/utils": "5.45.0", + "debug": "^4.3.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@typescript-eslint/parser": { + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.45.0.tgz", + "integrity": "sha512-brvs/WSM4fKUmF5Ot/gEve6qYiCMjm6w4HkHPfS6ZNmxTS0m0iNN4yOChImaCkqc1hRwFGqUyanMXuGal6oyyQ==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.45.0", + "@typescript-eslint/types": "5.45.0", + "@typescript-eslint/typescript-estree": "5.45.0", "debug": "^4.3.4" }, "dependencies": { @@ -8187,29 +8088,58 @@ } }, "@typescript-eslint/scope-manager": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.44.0.tgz", - "integrity": "sha512-2pKml57KusI0LAhgLKae9kwWeITZ7IsZs77YxyNyIVOwQ1kToyXRaJLl+uDEXzMN5hnobKUOo2gKntK9H1YL8g==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.45.0.tgz", + "integrity": "sha512-noDMjr87Arp/PuVrtvN3dXiJstQR1+XlQ4R1EvzG+NMgXi8CuMCXpb8JqNtFHKceVSQ985BZhfRdowJzbv4yKw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.44.0", - "@typescript-eslint/visitor-keys": "5.44.0" + "@typescript-eslint/types": "5.45.0", + "@typescript-eslint/visitor-keys": "5.45.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.45.0.tgz", + "integrity": "sha512-DY7BXVFSIGRGFZ574hTEyLPRiQIvI/9oGcN8t1A7f6zIs6ftbrU0nhyV26ZW//6f85avkwrLag424n+fkuoJ1Q==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "5.45.0", + "@typescript-eslint/utils": "5.45.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } }, "@typescript-eslint/types": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.44.0.tgz", - "integrity": "sha512-Tp+zDnHmGk4qKR1l+Y1rBvpjpm5tGXX339eAlRBDg+kgZkz9Bw+pqi4dyseOZMsGuSH69fYfPJCBKBrbPCxYFQ==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.45.0.tgz", + "integrity": "sha512-QQij+u/vgskA66azc9dCmx+rev79PzX8uDHpsqSjEFtfF2gBUTRCpvYMh2gw2ghkJabNkPlSUCimsyBEQZd1DA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.44.0.tgz", - "integrity": "sha512-M6Jr+RM7M5zeRj2maSfsZK2660HKAJawv4Ud0xT+yauyvgrsHu276VtXlKDFnEmhG+nVEd0fYZNXGoAgxwDWJw==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.0.tgz", + "integrity": "sha512-maRhLGSzqUpFcZgXxg1qc/+H0bT36lHK4APhp0AEUVrpSwXiRAomm/JGjSG+kNUio5kAa3uekCYu/47cnGn5EQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.44.0", - "@typescript-eslint/visitor-keys": "5.44.0", + "@typescript-eslint/types": "5.45.0", + "@typescript-eslint/visitor-keys": "5.45.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -8234,13 +8164,47 @@ } } }, - "@typescript-eslint/visitor-keys": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.44.0.tgz", - "integrity": "sha512-a48tLG8/4m62gPFbJ27FxwCOqPKxsb8KC3HkmYoq2As/4YyjQl1jDbRr1s63+g4FS/iIehjmN3L5UjmKva1HzQ==", + "@typescript-eslint/utils": { + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.45.0.tgz", + "integrity": "sha512-OUg2JvsVI1oIee/SwiejTot2OxwU8a7UfTFMOdlhD2y+Hl6memUSL4s98bpUTo8EpVEr0lmwlU7JSu/p2QpSvA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.44.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.45.0", + "@typescript-eslint/types": "5.45.0", + "@typescript-eslint/typescript-estree": "5.45.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, + "dependencies": { + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.0.tgz", + "integrity": "sha512-jc6Eccbn2RtQPr1s7th6jJWQHBHI6GBVQkCHoJFQ5UreaKm59Vxw+ynQUPPY2u2Amquc+7tmEoC2G52ApsGNNg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.45.0", "eslint-visitor-keys": "^3.3.0" } }, @@ -8540,6 +8504,7 @@ "version": "4.21.3", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "dev": true, "requires": { "caniuse-lite": "^1.0.30001370", "electron-to-chromium": "^1.4.202", @@ -8988,7 +8953,8 @@ "electron-to-chromium": { "version": "1.4.206", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.206.tgz", - "integrity": "sha512-h+Fadt1gIaQ06JaIiyqPsBjJ08fV5Q7md+V8bUvQW/9OvXfL2LRICTz2EcnnCP7QzrFTS6/27MRV6Bl9Yn97zA==" + "integrity": "sha512-h+Fadt1gIaQ06JaIiyqPsBjJ08fV5Q7md+V8bUvQW/9OvXfL2LRICTz2EcnnCP7QzrFTS6/27MRV6Bl9Yn97zA==", + "dev": true }, "emoji-regex": { "version": "9.2.2", @@ -9089,7 +9055,8 @@ "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true }, "escape-string-regexp": { "version": "1.0.5", @@ -9097,9 +9064,9 @@ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" }, "eslint": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.28.0.tgz", - "integrity": "sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==", + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", + "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", "dev": true, "requires": { "@eslint/eslintrc": "^1.3.3", @@ -9748,12 +9715,6 @@ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "peer": true - }, "get-intrinsic": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", @@ -10268,12 +10229,6 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", - "peer": true - }, "jsonp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/jsonp/-/jsonp-0.2.1.tgz", @@ -10824,6 +10779,12 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, "next": { "version": "12.3.1", "resolved": "https://registry.npmjs.org/next/-/next-12.3.1.tgz", @@ -10850,10 +10811,17 @@ "use-sync-external-store": "1.2.0" } }, + "next-translate": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/next-translate/-/next-translate-1.6.0.tgz", + "integrity": "sha512-rmRBYOwPHvokN+5uH3O/SZqfRUVORLkgt/097mSaq3+CWcEtXEjkVrfBZpYwZ8W3og4QVd+uphYX//qegnIbRg==", + "requires": {} + }, "node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true }, "normalize-path": { "version": "3.0.0", @@ -12171,6 +12139,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "dev": true, "requires": { "escalade": "^3.1.1", "picocolors": "^1.0.0" diff --git a/frontend/package.json b/frontend/package.json index d1ae205f9..257245d4e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -54,9 +54,12 @@ "devDependencies": { "@tailwindcss/typography": "^0.5.4", "@types/node": "18.11.9", + "@typescript-eslint/eslint-plugin": "^5.45.0", + "@typescript-eslint/parser": "^5.45.0", "autoprefixer": "^10.4.7", - "eslint": "^8.28.0", + "eslint": "^8.29.0", "eslint-config-next": "^13.0.5", + "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-simple-import-sort": "^8.0.0", "postcss": "^8.4.14", "prettier": "2.7.1", diff --git a/frontend/pages/api/auth/ChangePassword2.js b/frontend/pages/api/auth/ChangePassword2.js index 56db44ebf..b764e6067 100644 --- a/frontend/pages/api/auth/ChangePassword2.js +++ b/frontend/pages/api/auth/ChangePassword2.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -14,7 +13,7 @@ const changePassword2 = ({ verifier, clientProof, }) => { - return SecurityClient.fetchCall(PATH + "/api/v1/password/change-password", { + return SecurityClient.fetchCall("/api/v1/password/change-password", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/frontend/pages/api/auth/CheckAuth.js b/frontend/pages/api/auth/CheckAuth.js index 5ddcb6d9a..edd373614 100644 --- a/frontend/pages/api/auth/CheckAuth.js +++ b/frontend/pages/api/auth/CheckAuth.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient.js"; /** @@ -9,7 +8,7 @@ import SecurityClient from "~/utilities/SecurityClient.js"; * @returns */ const checkAuth = async (req, res) => { - return SecurityClient.fetchCall(PATH + "/api/v1/auth/checkAuth", { + return SecurityClient.fetchCall("/api/v1/auth/checkAuth", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/frontend/pages/api/auth/CheckEmailVerificationCode.js b/frontend/pages/api/auth/CheckEmailVerificationCode.js index aaf9205b6..83a64602a 100644 --- a/frontend/pages/api/auth/CheckEmailVerificationCode.js +++ b/frontend/pages/api/auth/CheckEmailVerificationCode.js @@ -1,5 +1,3 @@ -import { PATH } from "~/const"; - /** * This route check the verification code from the email that user just recieved * @param {*} email @@ -7,7 +5,7 @@ import { PATH } from "~/const"; * @returns */ const checkEmailVerificationCode = (email, code) => { - return fetch(PATH + "/api/v1/signup/email/verify", { + return fetch("/api/v1/signup/email/verify", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/frontend/pages/api/auth/CompleteAccountInformationSignup.js b/frontend/pages/api/auth/CompleteAccountInformationSignup.js index b315510e5..21406e381 100644 --- a/frontend/pages/api/auth/CompleteAccountInformationSignup.js +++ b/frontend/pages/api/auth/CompleteAccountInformationSignup.js @@ -1,5 +1,3 @@ -import { PATH } from "~/const"; - /** * This function is called in the end of the signup process. * It sends all the necessary nformation to the server. @@ -28,7 +26,7 @@ const completeAccountInformationSignup = ({ verifier, token, }) => { - return fetch(PATH + "/api/v1/signup/complete-account/signup", { + return fetch("/api/v1/signup/complete-account/signup", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/frontend/pages/api/auth/CompleteAccountInformationSignupInvite.js b/frontend/pages/api/auth/CompleteAccountInformationSignupInvite.js index 4af4381bd..a205e6f59 100644 --- a/frontend/pages/api/auth/CompleteAccountInformationSignupInvite.js +++ b/frontend/pages/api/auth/CompleteAccountInformationSignupInvite.js @@ -1,5 +1,3 @@ -import { PATH } from "~/const"; - /** * This function is called in the end of the signup process. * It sends all the necessary nformation to the server. @@ -26,7 +24,7 @@ const completeAccountInformationSignupInvite = ({ verifier, token, }) => { - return fetch(PATH + "/api/v1/signup/complete-account/invite", { + return fetch("/api/v1/signup/complete-account/invite", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/frontend/pages/api/auth/IssueBackupPrivateKey.js b/frontend/pages/api/auth/IssueBackupPrivateKey.js index a4cff8041..9a31f7b00 100644 --- a/frontend/pages/api/auth/IssueBackupPrivateKey.js +++ b/frontend/pages/api/auth/IssueBackupPrivateKey.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -13,7 +12,7 @@ const issueBackupPrivateKey = ({ clientProof, }) => { return SecurityClient.fetchCall( - PATH + "/api/v1/password/backup-private-key", + "/api/v1/password/backup-private-key", { method: "POST", headers: { @@ -32,8 +31,8 @@ const issueBackupPrivateKey = ({ if (res.status == 200) { return res; } else { - return res; console.log("Failed to issue the backup key"); + return res; } }); }; diff --git a/frontend/pages/api/auth/Login1.js b/frontend/pages/api/auth/Login1.js index 697933812..3f3493f77 100644 --- a/frontend/pages/api/auth/Login1.js +++ b/frontend/pages/api/auth/Login1.js @@ -1,5 +1,3 @@ -import { PATH } from "~/const"; - /** * This is the first step of the login process (pake) * @param {*} email @@ -7,7 +5,7 @@ import { PATH } from "~/const"; * @returns */ const login1 = (email, clientPublicKey) => { - return fetch(PATH + "/api/v1/auth/login1", { + return fetch("/api/v1/auth/login1", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/frontend/pages/api/auth/Login2.js b/frontend/pages/api/auth/Login2.js index fdb48342e..923e261df 100644 --- a/frontend/pages/api/auth/Login2.js +++ b/frontend/pages/api/auth/Login2.js @@ -1,5 +1,3 @@ -import { PATH } from "~/const"; - /** * This is the second step of the login process * @param {*} email @@ -7,7 +5,7 @@ import { PATH } from "~/const"; * @returns */ const login2 = (email, clientProof) => { - return fetch(PATH + "/api/v1/auth/login2", { + return fetch("/api/v1/auth/login2", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/frontend/pages/api/auth/Logout.js b/frontend/pages/api/auth/Logout.ts similarity index 80% rename from frontend/pages/api/auth/Logout.js rename to frontend/pages/api/auth/Logout.ts index c5e3e4468..4dbcb7bca 100644 --- a/frontend/pages/api/auth/Logout.js +++ b/frontend/pages/api/auth/Logout.ts @@ -1,22 +1,18 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** * This route logs the user out. Note: the user should authorized to do this. * We first try to log out - if the authorization fails (response.status = 401), we refetch the new token, and then retry - * @param {*} req - * @param {*} res - * @returns */ -const logout = async (req, res) => { - return SecurityClient.fetchCall(PATH + "/api/v1/auth/logout", { +const logout = async () => { + return SecurityClient.fetchCall("/api/v1/auth/logout", { method: "POST", headers: { "Content-Type": "application/json", }, credentials: "include", }).then((res) => { - if (res.status == 200) { + if (res?.status == 200) { SecurityClient.setToken(""); // Delete the cookie by not setting a value; Alternatively clear the local storage localStorage.setItem("publicKey", ""); diff --git a/frontend/pages/api/auth/SRP1.js b/frontend/pages/api/auth/SRP1.js index 310066040..b0fefb857 100644 --- a/frontend/pages/api/auth/SRP1.js +++ b/frontend/pages/api/auth/SRP1.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -7,7 +6,7 @@ import SecurityClient from "~/utilities/SecurityClient"; * @returns */ const SRP1 = ({ clientPublicKey }) => { - return SecurityClient.fetchCall(PATH + "/api/v1/password/srp1", { + return SecurityClient.fetchCall("/api/v1/password/srp1", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/frontend/pages/api/auth/SendVerificationEmail.js b/frontend/pages/api/auth/SendVerificationEmail.js index 981c55936..ae952852d 100644 --- a/frontend/pages/api/auth/SendVerificationEmail.js +++ b/frontend/pages/api/auth/SendVerificationEmail.js @@ -1,11 +1,9 @@ -import { PATH } from "~/const"; - /** * This route send the verification email to the user's email (contains a 6-digit verification code) * @param {*} email */ const sendVerificationEmail = (email) => { - fetch(PATH + "/api/v1/signup/email/signup", { + fetch("/api/v1/signup/email/signup", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/frontend/pages/api/auth/Token.js b/frontend/pages/api/auth/Token.js index 29164aec3..c3e5fd958 100644 --- a/frontend/pages/api/auth/Token.js +++ b/frontend/pages/api/auth/Token.js @@ -1,7 +1,5 @@ -import { PATH } from "~/const"; - const token = async (req, res) => { - return fetch(PATH + "/api/v1/auth/token", { + return fetch("/api/v1/auth/token", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/frontend/pages/api/auth/VerifySignupInvite.js b/frontend/pages/api/auth/VerifySignupInvite.js index 19fe229f9..2a9ba4dcd 100644 --- a/frontend/pages/api/auth/VerifySignupInvite.js +++ b/frontend/pages/api/auth/VerifySignupInvite.js @@ -1,5 +1,3 @@ -import { PATH } from "~/const"; - /** * This route verifies the signup invite link * @param {*} email @@ -7,7 +5,7 @@ import { PATH } from "~/const"; * @returns */ const verifySignupInvite = ({ email, code }) => { - return fetch(PATH + "/api/v1/invite-org/verify", { + return fetch("/api/v1/invite-org/verify", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/frontend/pages/api/auth/publicKeyInfisical.js b/frontend/pages/api/auth/publicKeyInfisical.js index 070096fb4..76cad1726 100644 --- a/frontend/pages/api/auth/publicKeyInfisical.js +++ b/frontend/pages/api/auth/publicKeyInfisical.js @@ -1,5 +1,3 @@ -import { PATH } from "~/const"; - /** * This route lets us get the public key of infisical. Th euser doesn't have to be authenticated since this is just the public key. * @param {*} req @@ -7,7 +5,7 @@ import { PATH } from "~/const"; * @returns */ const publicKeyInfisical = (req, res) => { - return fetch(PATH + "/api/v1/key/publicKey/infisical", { + return fetch("/api/v1/key/publicKey/infisical", { method: "GET", headers: { "Content-Type": "application/json", diff --git a/frontend/pages/api/files/GetSecrets.js b/frontend/pages/api/files/GetSecrets.js index 6f5bf1d97..fa91d0962 100644 --- a/frontend/pages/api/files/GetSecrets.js +++ b/frontend/pages/api/files/GetSecrets.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient.js"; /** @@ -9,7 +8,6 @@ import SecurityClient from "~/utilities/SecurityClient.js"; */ const getSecrets = async (workspaceId, env) => { return SecurityClient.fetchCall( - PATH + "/api/v1/secret/" + workspaceId + "?" + diff --git a/frontend/pages/api/files/UploadSecrets.js b/frontend/pages/api/files/UploadSecrets.js index 2b6dd0722..c2f94e64d 100644 --- a/frontend/pages/api/files/UploadSecrets.js +++ b/frontend/pages/api/files/UploadSecrets.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -8,7 +7,7 @@ import SecurityClient from "~/utilities/SecurityClient"; * @returns */ const uploadSecrets = async ({ workspaceId, secrets, keys, environment }) => { - return SecurityClient.fetchCall(PATH + "/api/v1/secret/" + workspaceId, { + return SecurityClient.fetchCall("/api/v1/secret/" + workspaceId, { method: "POST", headers: { "Content-Type": "application/json", diff --git a/frontend/pages/api/integrations/ChangeHerokuConfigVars.js b/frontend/pages/api/integrations/ChangeHerokuConfigVars.js index 6075ad516..118848ae6 100644 --- a/frontend/pages/api/integrations/ChangeHerokuConfigVars.js +++ b/frontend/pages/api/integrations/ChangeHerokuConfigVars.js @@ -1,9 +1,8 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; const changeHerokuConfigVars = ({ integrationId, key, secrets }) => { return SecurityClient.fetchCall( - PATH + "/api/v1/integration/" + integrationId + "/sync", + "/api/v1/integration/" + integrationId + "/sync", { method: "POST", headers: { diff --git a/frontend/pages/api/integrations/DeleteIntegration.js b/frontend/pages/api/integrations/DeleteIntegration.js index 5b3dd6ea5..7698d7eae 100644 --- a/frontend/pages/api/integrations/DeleteIntegration.js +++ b/frontend/pages/api/integrations/DeleteIntegration.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -8,7 +7,7 @@ import SecurityClient from "~/utilities/SecurityClient"; */ const deleteIntegration = ({ integrationId }) => { return SecurityClient.fetchCall( - PATH + "/api/v1/integration/" + integrationId, + "/api/v1/integration/" + integrationId, { method: "DELETE", headers: { diff --git a/frontend/pages/api/integrations/DeleteIntegrationAuth.js b/frontend/pages/api/integrations/DeleteIntegrationAuth.js index 1d8566b41..eb6106e8e 100644 --- a/frontend/pages/api/integrations/DeleteIntegrationAuth.js +++ b/frontend/pages/api/integrations/DeleteIntegrationAuth.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -8,7 +7,7 @@ import SecurityClient from "~/utilities/SecurityClient"; */ const deleteIntegrationAuth = ({ integrationAuthId }) => { return SecurityClient.fetchCall( - PATH + "/api/v1/integration-auth/" + integrationAuthId, + "/api/v1/integration-auth/" + integrationAuthId, { method: "DELETE", headers: { diff --git a/frontend/pages/api/integrations/GetIntegrationApps.js b/frontend/pages/api/integrations/GetIntegrationApps.js index e049c3006..dc0bfb4b6 100644 --- a/frontend/pages/api/integrations/GetIntegrationApps.js +++ b/frontend/pages/api/integrations/GetIntegrationApps.js @@ -1,9 +1,8 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; const getIntegrationApps = ({ integrationAuthId }) => { return SecurityClient.fetchCall( - PATH + "/api/v1/integration-auth/" + integrationAuthId + "/apps", + "/api/v1/integration-auth/" + integrationAuthId + "/apps", { method: "GET", headers: { diff --git a/frontend/pages/api/integrations/GetIntegrations.js b/frontend/pages/api/integrations/GetIntegrations.js index 90e438a6c..401c80d3b 100644 --- a/frontend/pages/api/integrations/GetIntegrations.js +++ b/frontend/pages/api/integrations/GetIntegrations.js @@ -1,8 +1,7 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; const getIntegrations = () => { - return SecurityClient.fetchCall(PATH + "/api/v1/integration/integrations", { + return SecurityClient.fetchCall("/api/v1/integration/integrations", { method: "GET", headers: { "Content-Type": "application/json", diff --git a/frontend/pages/api/integrations/StartIntegration.js b/frontend/pages/api/integrations/StartIntegration.js index a52fb1855..a4e8b0b02 100644 --- a/frontend/pages/api/integrations/StartIntegration.js +++ b/frontend/pages/api/integrations/StartIntegration.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -8,7 +7,7 @@ import SecurityClient from "~/utilities/SecurityClient"; */ const startIntegration = ({ integrationId, appName, environment }) => { return SecurityClient.fetchCall( - PATH + "/api/v1/integration/" + integrationId, + "/api/v1/integration/" + integrationId, { method: "PATCH", headers: { diff --git a/frontend/pages/api/integrations/authorizeIntegration.js b/frontend/pages/api/integrations/authorizeIntegration.js index 38dbcc713..b9a1d3995 100644 --- a/frontend/pages/api/integrations/authorizeIntegration.js +++ b/frontend/pages/api/integrations/authorizeIntegration.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -8,7 +7,7 @@ import SecurityClient from "~/utilities/SecurityClient"; */ const AuthorizeIntegration = ({ workspaceId, code, integration }) => { return SecurityClient.fetchCall( - PATH + "/api/v1/integration-auth/oauth-token", + "/api/v1/integration-auth/oauth-token", { method: "POST", headers: { diff --git a/frontend/pages/api/integrations/getWorkspaceAuthorizations.js b/frontend/pages/api/integrations/getWorkspaceAuthorizations.js index c05ae6ffc..ad8c4732b 100644 --- a/frontend/pages/api/integrations/getWorkspaceAuthorizations.js +++ b/frontend/pages/api/integrations/getWorkspaceAuthorizations.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -8,7 +7,7 @@ import SecurityClient from "~/utilities/SecurityClient"; */ const getWorkspaceAuthorizations = ({ workspaceId }) => { return SecurityClient.fetchCall( - PATH + "/api/v1/workspace/" + workspaceId + "/authorizations", + "/api/v1/workspace/" + workspaceId + "/authorizations", { method: "GET", headers: { diff --git a/frontend/pages/api/integrations/getWorkspaceIntegrations.js b/frontend/pages/api/integrations/getWorkspaceIntegrations.js index 899aeaa8a..22470e4be 100644 --- a/frontend/pages/api/integrations/getWorkspaceIntegrations.js +++ b/frontend/pages/api/integrations/getWorkspaceIntegrations.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -8,7 +7,7 @@ import SecurityClient from "~/utilities/SecurityClient"; */ const getWorkspaceIntegrations = ({ workspaceId }) => { return SecurityClient.fetchCall( - PATH + "/api/v1/workspace/" + workspaceId + "/integrations", + "/api/v1/workspace/" + workspaceId + "/integrations", { method: "GET", headers: { diff --git a/frontend/pages/api/organization/GetOrg.js b/frontend/pages/api/organization/GetOrg.ts similarity index 64% rename from frontend/pages/api/organization/GetOrg.js rename to frontend/pages/api/organization/GetOrg.ts index 85414470b..ecb07bd2d 100644 --- a/frontend/pages/api/organization/GetOrg.js +++ b/frontend/pages/api/organization/GetOrg.ts @@ -1,20 +1,18 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** * This route lets us get info about a certain org - * @param {*} req - * @param {*} res + * @param {string} orgId - the organization ID * @returns */ -const getOrganization = (req, res) => { - return SecurityClient.fetchCall(PATH + "/api/v1/organization/" + req.orgId, { +const getOrganization = ({ orgId }: { orgId: string; }) => { + return SecurityClient.fetchCall("/api/v1/organization/" + orgId, { method: "GET", headers: { "Content-Type": "application/json", }, }).then(async (res) => { - if (res.status == 200) { + if (res?.status == 200) { return (await res.json()).organization; } else { console.log("Failed to get org info"); diff --git a/frontend/pages/api/organization/GetOrgProjects.js b/frontend/pages/api/organization/GetOrgProjects.js index 1ff80a3eb..656fea18f 100644 --- a/frontend/pages/api/organization/GetOrgProjects.js +++ b/frontend/pages/api/organization/GetOrgProjects.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -9,7 +8,7 @@ import SecurityClient from "~/utilities/SecurityClient"; */ const getOrganizationProjects = (req, res) => { return SecurityClient.fetchCall( - PATH + "/api/organization/" + req.orgId + "/workspaces", + "/api/organization/" + req.orgId + "/workspaces", { method: "GET", headers: { diff --git a/frontend/pages/api/organization/GetOrgSubscription.js b/frontend/pages/api/organization/GetOrgSubscription.js index a983c15ed..97c6f4e5c 100644 --- a/frontend/pages/api/organization/GetOrgSubscription.js +++ b/frontend/pages/api/organization/GetOrgSubscription.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -9,7 +8,7 @@ import SecurityClient from "~/utilities/SecurityClient"; */ const getOrganizationSubscriptions = (req, res) => { return SecurityClient.fetchCall( - PATH + "/api/v1/organization/" + req.orgId + "/subscriptions", + "/api/v1/organization/" + req.orgId + "/subscriptions", { method: "GET", headers: { diff --git a/frontend/pages/api/organization/GetOrgUserProjects.js b/frontend/pages/api/organization/GetOrgUserProjects.js index fc51d18ce..c09193456 100644 --- a/frontend/pages/api/organization/GetOrgUserProjects.js +++ b/frontend/pages/api/organization/GetOrgUserProjects.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -9,7 +8,7 @@ import SecurityClient from "~/utilities/SecurityClient"; */ const getOrganizationUserProjects = (req, res) => { return SecurityClient.fetchCall( - PATH + "/api/v1/organization/" + req.orgId + "/my-workspaces", + "/api/v1/organization/" + req.orgId + "/my-workspaces", { method: "GET", headers: { diff --git a/frontend/pages/api/organization/GetOrgUsers.js b/frontend/pages/api/organization/GetOrgUsers.ts similarity index 67% rename from frontend/pages/api/organization/GetOrgUsers.js rename to frontend/pages/api/organization/GetOrgUsers.ts index 287b07bec..53b9518cf 100644 --- a/frontend/pages/api/organization/GetOrgUsers.js +++ b/frontend/pages/api/organization/GetOrgUsers.ts @@ -1,15 +1,14 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** * This route lets us get all the users in an org. - * @param {*} req - * @param {*} res + * @param {object} obj + * @param {string} obj.orgId - organization Id * @returns */ -const getOrganizationUsers = (req, res) => { +const getOrganizationUsers = ({ orgId }: { orgId: string; }) => { return SecurityClient.fetchCall( - PATH + "/api/v1/organization/" + req.orgId + "/users", + "/api/v1/organization/" + orgId + "/users", { method: "GET", headers: { @@ -17,7 +16,7 @@ const getOrganizationUsers = (req, res) => { }, } ).then(async (res) => { - if (res.status == 200) { + if (res?.status == 200) { return (await res.json()).users; } else { console.log("Failed to get org users"); diff --git a/frontend/pages/api/organization/StripeRedirect.js b/frontend/pages/api/organization/StripeRedirect.js index 6d28d464b..e8911cf53 100644 --- a/frontend/pages/api/organization/StripeRedirect.js +++ b/frontend/pages/api/organization/StripeRedirect.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -9,7 +8,7 @@ import SecurityClient from "~/utilities/SecurityClient"; */ const StripeRedirect = ({ orgId }) => { return SecurityClient.fetchCall( - PATH + "/api/v1/organization/" + orgId + "/customer-portal-session", + "/api/v1/organization/" + orgId + "/customer-portal-session", { method: "POST", headers: { diff --git a/frontend/pages/api/organization/addIncidentContact.js b/frontend/pages/api/organization/addIncidentContact.js index b1b3d5fa9..0e6068a1a 100644 --- a/frontend/pages/api/organization/addIncidentContact.js +++ b/frontend/pages/api/organization/addIncidentContact.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -8,7 +7,7 @@ import SecurityClient from "~/utilities/SecurityClient"; */ const addIncidentContact = (organizationId, email) => { return SecurityClient.fetchCall( - PATH + "/api/v1/organization/" + organizationId + "/incidentContactOrg", + "/api/v1/organization/" + organizationId + "/incidentContactOrg", { method: "POST", headers: { diff --git a/frontend/pages/api/organization/addUserToOrg.js b/frontend/pages/api/organization/addUserToOrg.js index ccee48da3..15f22cff9 100644 --- a/frontend/pages/api/organization/addUserToOrg.js +++ b/frontend/pages/api/organization/addUserToOrg.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -8,7 +7,7 @@ import SecurityClient from "~/utilities/SecurityClient"; * @returns */ const addUserToOrg = (email, orgId) => { - return SecurityClient.fetchCall(PATH + "/api/v1/invite-org/signup", { + return SecurityClient.fetchCall("/api/v1/invite-org/signup", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/frontend/pages/api/organization/deleteIncidentContact.js b/frontend/pages/api/organization/deleteIncidentContact.js index a26335d6d..f6e57c590 100644 --- a/frontend/pages/api/organization/deleteIncidentContact.js +++ b/frontend/pages/api/organization/deleteIncidentContact.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -8,7 +7,7 @@ import SecurityClient from "~/utilities/SecurityClient"; */ const deleteIncidentContact = (organizaionId, email) => { return SecurityClient.fetchCall( - PATH + "/api/v1/organization/" + organizaionId + "/incidentContactOrg", + "/api/v1/organization/" + organizaionId + "/incidentContactOrg", { method: "DELETE", headers: { diff --git a/frontend/pages/api/organization/deleteUserFromOrganization.js b/frontend/pages/api/organization/deleteUserFromOrganization.js index 269c9b2ed..66ea84793 100644 --- a/frontend/pages/api/organization/deleteUserFromOrganization.js +++ b/frontend/pages/api/organization/deleteUserFromOrganization.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -8,7 +7,7 @@ import SecurityClient from "~/utilities/SecurityClient"; */ const deleteUserFromOrganization = (membershipId) => { return SecurityClient.fetchCall( - PATH + "/api/v1/membership-org/" + membershipId, + "/api/v1/membership-org/" + membershipId, { method: "DELETE", headers: { diff --git a/frontend/pages/api/organization/getIncidentContacts.js b/frontend/pages/api/organization/getIncidentContacts.js index f2867e63c..4f3f61a48 100644 --- a/frontend/pages/api/organization/getIncidentContacts.js +++ b/frontend/pages/api/organization/getIncidentContacts.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -8,7 +7,7 @@ import SecurityClient from "~/utilities/SecurityClient"; */ const getIncidentContacts = (organizationId) => { return SecurityClient.fetchCall( - PATH + "/api/v1/organization/" + organizationId + "/incidentContactOrg", + "/api/v1/organization/" + organizationId + "/incidentContactOrg", { method: "GET", headers: { diff --git a/frontend/pages/api/organization/getOrgs.js b/frontend/pages/api/organization/getOrgs.ts similarity index 66% rename from frontend/pages/api/organization/getOrgs.js rename to frontend/pages/api/organization/getOrgs.ts index 5f28f913d..cc655642f 100644 --- a/frontend/pages/api/organization/getOrgs.js +++ b/frontend/pages/api/organization/getOrgs.ts @@ -1,20 +1,17 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** * This route lets us get the all the orgs of a certain user. - * @param {*} req - * @param {*} res * @returns */ -const getOrganizations = (req, res) => { - return SecurityClient.fetchCall(PATH + "/api/v1/organization", { +const getOrganizations = () => { + return SecurityClient.fetchCall("/api/v1/organization", { method: "GET", headers: { "Content-Type": "application/json", }, }).then(async (res) => { - if (res.status == 200) { + if (res?.status == 200) { return (await res.json()).organizations; } else { console.log("Failed to get orgs of a user"); diff --git a/frontend/pages/api/organization/renameOrg.js b/frontend/pages/api/organization/renameOrg.js index a33c6d3a5..a9d80a3a5 100644 --- a/frontend/pages/api/organization/renameOrg.js +++ b/frontend/pages/api/organization/renameOrg.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -9,7 +8,7 @@ import SecurityClient from "~/utilities/SecurityClient"; */ const renameOrg = (orgId, newOrgName) => { return SecurityClient.fetchCall( - PATH + "/api/v1/organization/" + orgId + "/name", + "/api/v1/organization/" + orgId + "/name", { method: "PATCH", headers: { diff --git a/frontend/pages/api/serviceToken/addServiceToken.js b/frontend/pages/api/serviceToken/addServiceToken.js index 2b4a8e46e..4c6031db8 100644 --- a/frontend/pages/api/serviceToken/addServiceToken.js +++ b/frontend/pages/api/serviceToken/addServiceToken.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -15,7 +14,7 @@ const addServiceToken = ({ encryptedKey, nonce, }) => { - return SecurityClient.fetchCall(PATH + "/api/v1/service-token/", { + return SecurityClient.fetchCall("/api/v1/service-token/", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/frontend/pages/api/serviceToken/getServiceTokens.js b/frontend/pages/api/serviceToken/getServiceTokens.js index 3928076cc..79c6a7fc0 100644 --- a/frontend/pages/api/serviceToken/getServiceTokens.js +++ b/frontend/pages/api/serviceToken/getServiceTokens.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -8,7 +7,7 @@ import SecurityClient from "~/utilities/SecurityClient"; */ const getServiceTokens = ({ workspaceId }) => { return SecurityClient.fetchCall( - PATH + "/api/v1/workspace/" + workspaceId + "/service-tokens", + "/api/v1/workspace/" + workspaceId + "/service-tokens", { method: "GET", headers: { diff --git a/frontend/pages/api/user/getUser.js b/frontend/pages/api/user/getUser.ts similarity index 64% rename from frontend/pages/api/user/getUser.js rename to frontend/pages/api/user/getUser.ts index baca9239b..4bfebc5fe 100644 --- a/frontend/pages/api/user/getUser.js +++ b/frontend/pages/api/user/getUser.ts @@ -1,20 +1,16 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** * This route gets the information about a specific user. - * @param {*} req - * @param {*} res - * @returns */ -const getUser = (req, res) => { - return SecurityClient.fetchCall(PATH + "/api/v1/user", { +const getUser = () => { + return SecurityClient.fetchCall("/api/v1/user", { method: "GET", headers: { "Content-Type": "application/json", }, }).then(async (res) => { - if (res.status == 200) { + if (res?.status == 200) { return (await res.json()).user; } else { console.log("Failed to get user info"); diff --git a/frontend/pages/api/userActions/checkUserAction.js b/frontend/pages/api/userActions/checkUserAction.js index 981fa74ee..dfe217856 100644 --- a/frontend/pages/api/userActions/checkUserAction.js +++ b/frontend/pages/api/userActions/checkUserAction.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -9,7 +8,6 @@ import SecurityClient from "~/utilities/SecurityClient"; */ const checkUserAction = ({ action }) => { return SecurityClient.fetchCall( - PATH + "/api/v1/user-action" + "?" + new URLSearchParams({ diff --git a/frontend/pages/api/userActions/registerUserAction.js b/frontend/pages/api/userActions/registerUserAction.js index 98a80627c..619886d89 100644 --- a/frontend/pages/api/userActions/registerUserAction.js +++ b/frontend/pages/api/userActions/registerUserAction.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -7,7 +6,7 @@ import SecurityClient from "~/utilities/SecurityClient"; * @returns */ const registerUserAction = ({ action }) => { - return SecurityClient.fetchCall(PATH + "/api/v1/user-action", { + return SecurityClient.fetchCall("/api/v1/user-action", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/frontend/pages/api/workspace/addUserToWorkspace.js b/frontend/pages/api/workspace/addUserToWorkspace.js index d6caa30e4..86e991389 100644 --- a/frontend/pages/api/workspace/addUserToWorkspace.js +++ b/frontend/pages/api/workspace/addUserToWorkspace.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -9,7 +8,7 @@ import SecurityClient from "~/utilities/SecurityClient"; */ const addUserToWorkspace = (email, workspaceId) => { return SecurityClient.fetchCall( - PATH + "/api/v1/workspace/" + workspaceId + "/invite-signup", + "/api/v1/workspace/" + workspaceId + "/invite-signup", { method: "POST", headers: { diff --git a/frontend/pages/api/workspace/changeUserRoleInWorkspace.js b/frontend/pages/api/workspace/changeUserRoleInWorkspace.js index 5f37e674f..a93e22181 100644 --- a/frontend/pages/api/workspace/changeUserRoleInWorkspace.js +++ b/frontend/pages/api/workspace/changeUserRoleInWorkspace.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -9,7 +8,7 @@ import SecurityClient from "~/utilities/SecurityClient"; */ const changeUserRoleInWorkspace = (membershipId, role) => { return SecurityClient.fetchCall( - PATH + "/api/v1/membership/" + membershipId + "/change-role", + "/api/v1/membership/" + membershipId + "/change-role", { method: "POST", headers: { diff --git a/frontend/pages/api/workspace/createWorkspace.js b/frontend/pages/api/workspace/createWorkspace.ts similarity index 54% rename from frontend/pages/api/workspace/createWorkspace.js rename to frontend/pages/api/workspace/createWorkspace.ts index 1451249b7..0cedb0a57 100644 --- a/frontend/pages/api/workspace/createWorkspace.js +++ b/frontend/pages/api/workspace/createWorkspace.ts @@ -1,13 +1,13 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** - * This route creates a new workspace for a user. - * @param {*} workspaceName + * This route creates a new workspace for a user within a certain organization. + * @param {string} workspaceName - project Name + * @param {string} organizationId - org ID * @returns */ -const createWorkspace = (workspaceName, organizationId) => { - return SecurityClient.fetchCall(PATH + "/api/v1/workspace", { +const createWorkspace = ( { workspaceName, organizationId }: { workspaceName: string; organizationId: string; }) => { + return SecurityClient.fetchCall("/api/v1/workspace", { method: "POST", headers: { "Content-Type": "application/json", @@ -17,7 +17,7 @@ const createWorkspace = (workspaceName, organizationId) => { organizationId: organizationId, }), }).then(async (res) => { - if (res.status == 200) { + if (res?.status == 200) { return (await res.json()).workspace; } else { console.log("Failed to create a project"); diff --git a/frontend/pages/api/workspace/deleteUserFromWorkspace.js b/frontend/pages/api/workspace/deleteUserFromWorkspace.js index 83389c281..bad8beced 100644 --- a/frontend/pages/api/workspace/deleteUserFromWorkspace.js +++ b/frontend/pages/api/workspace/deleteUserFromWorkspace.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -7,7 +6,7 @@ import SecurityClient from "~/utilities/SecurityClient"; * @returns */ const deleteUserFromWorkspace = (membershipId) => { - return SecurityClient.fetchCall(PATH + "/api/v1/membership/" + membershipId, { + return SecurityClient.fetchCall("/api/v1/membership/" + membershipId, { method: "DELETE", headers: { "Content-Type": "application/json", diff --git a/frontend/pages/api/workspace/deleteWorkspace.js b/frontend/pages/api/workspace/deleteWorkspace.js index c84ebe756..33d34844d 100644 --- a/frontend/pages/api/workspace/deleteWorkspace.js +++ b/frontend/pages/api/workspace/deleteWorkspace.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -7,7 +6,7 @@ import SecurityClient from "~/utilities/SecurityClient"; * @returns */ const deleteWorkspace = (workspaceId) => { - return SecurityClient.fetchCall(PATH + "/api/v1/workspace/" + workspaceId, { + return SecurityClient.fetchCall("/api/v1/workspace/" + workspaceId, { method: "DELETE", headers: { "Content-Type": "application/json", diff --git a/frontend/pages/api/workspace/getLatestFileKey.js b/frontend/pages/api/workspace/getLatestFileKey.ts similarity index 71% rename from frontend/pages/api/workspace/getLatestFileKey.js rename to frontend/pages/api/workspace/getLatestFileKey.ts index e1fab97c6..86ecb7456 100644 --- a/frontend/pages/api/workspace/getLatestFileKey.js +++ b/frontend/pages/api/workspace/getLatestFileKey.ts @@ -1,14 +1,13 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** * Get the latest key pairs from a certain workspace - * @param {*} workspaceId + * @param {string} workspaceId * @returns */ -const getLatestFileKey = (workspaceId) => { +const getLatestFileKey = ({ workspaceId } : { workspaceId: string; }) => { return SecurityClient.fetchCall( - PATH + "/api/v1/key/" + workspaceId + "/latest", + "/api/v1/key/" + workspaceId + "/latest", { method: "GET", headers: { @@ -16,7 +15,7 @@ const getLatestFileKey = (workspaceId) => { }, } ).then(async (res) => { - if (res.status == 200) { + if (res?.status == 200) { return await res.json(); } else { console.log("Failed to get the latest key pairs for a certain project"); diff --git a/frontend/pages/api/workspace/getWorkspaceInfo.js b/frontend/pages/api/workspace/getProjectInfo.ts similarity index 62% rename from frontend/pages/api/workspace/getWorkspaceInfo.js rename to frontend/pages/api/workspace/getProjectInfo.ts index 449cbd54b..c6eef9dce 100644 --- a/frontend/pages/api/workspace/getWorkspaceInfo.js +++ b/frontend/pages/api/workspace/getProjectInfo.ts @@ -1,15 +1,13 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** * This route lets us get the information of a certain project. - * @param {*} req - * @param {*} res + * @param {*} projectId - project ID (we renamed workspaces to projects in the app) * @returns */ -const getWorkspaceInfo = (req, res) => { +const getProjectInfo = ({ projectId }: { projectId: string; }) => { return SecurityClient.fetchCall( - PATH + "/api/v1/workspace/" + req.workspaceId, + "/api/v1/workspace/" + projectId, { method: "GET", headers: { @@ -17,7 +15,7 @@ const getWorkspaceInfo = (req, res) => { }, } ).then(async (res) => { - if (res.status == 200) { + if (res?.status == 200) { return (await res.json()).workspace; } else { console.log("Failed to get project info"); @@ -25,4 +23,4 @@ const getWorkspaceInfo = (req, res) => { }); }; -export default getWorkspaceInfo; +export default getProjectInfo; diff --git a/frontend/pages/api/workspace/getWorkspaceKeys.js b/frontend/pages/api/workspace/getWorkspaceKeys.ts similarity index 71% rename from frontend/pages/api/workspace/getWorkspaceKeys.js rename to frontend/pages/api/workspace/getWorkspaceKeys.ts index c94cb786e..09b468662 100644 --- a/frontend/pages/api/workspace/getWorkspaceKeys.js +++ b/frontend/pages/api/workspace/getWorkspaceKeys.ts @@ -1,15 +1,13 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** * This route lets us get the public keys of everyone in your workspace. - * @param {*} req - * @param {*} res + * @param {string} workspaceId * @returns */ -const getWorkspaceKeys = (req, res) => { +const getWorkspaceKeys = ({ workspaceId }: { workspaceId: string; }) => { return SecurityClient.fetchCall( - PATH + "/api/v1/workspace/" + req.workspaceId + "/keys", + "/api/v1/workspace/" + workspaceId + "/keys", { method: "GET", headers: { @@ -17,7 +15,7 @@ const getWorkspaceKeys = (req, res) => { }, } ).then(async (res) => { - if (res.status == 200) { + if (res?.status == 200) { return (await res.json()).publicKeys; } else { console.log("Failed to get the public keys of everyone in the workspace"); diff --git a/frontend/pages/api/workspace/getWorkspaceUsers.js b/frontend/pages/api/workspace/getWorkspaceUsers.ts similarity index 69% rename from frontend/pages/api/workspace/getWorkspaceUsers.js rename to frontend/pages/api/workspace/getWorkspaceUsers.ts index b7c42bf88..9805c2695 100644 --- a/frontend/pages/api/workspace/getWorkspaceUsers.js +++ b/frontend/pages/api/workspace/getWorkspaceUsers.ts @@ -1,15 +1,13 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** * This route lets us get all the users in the workspace. - * @param {*} req - * @param {*} res + * @param {string} workspaceId - workspace ID * @returns */ -const getWorkspaceUsers = (req, res) => { +const getWorkspaceUsers = ({ workspaceId }: { workspaceId: string; }) => { return SecurityClient.fetchCall( - PATH + "/api/v1/workspace/" + req.workspaceId + "/users", + "/api/v1/workspace/" + workspaceId + "/users", { method: "GET", headers: { @@ -17,7 +15,7 @@ const getWorkspaceUsers = (req, res) => { }, } ).then(async (res) => { - if (res.status == 200) { + if (res?.status == 200) { return (await res.json()).users; } else { console.log("Failed to get Project Users"); diff --git a/frontend/pages/api/workspace/getWorkspaces.js b/frontend/pages/api/workspace/getWorkspaces.ts similarity index 55% rename from frontend/pages/api/workspace/getWorkspaces.js rename to frontend/pages/api/workspace/getWorkspaces.ts index d3b276eb7..9427c03d0 100644 --- a/frontend/pages/api/workspace/getWorkspaces.js +++ b/frontend/pages/api/workspace/getWorkspaces.ts @@ -1,20 +1,17 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** - * This route lets us get the public keys of everyone in your workspace. - * @param {*} req - * @param {*} res + * This route lets us get the workspaces of a certain user * @returns */ -const getWorkspaces = (req, res) => { - return SecurityClient.fetchCall(PATH + "/api/v1/workspace", { +const getWorkspaces = () => { + return SecurityClient.fetchCall("/api/v1/workspace", { method: "GET", headers: { "Content-Type": "application/json", }, }).then(async (res) => { - if (res.status == 200) { + if (res?.status == 200) { return (await res.json()).workspaces; } else { console.log("Failed to get projects"); diff --git a/frontend/pages/api/workspace/renameWorkspace.js b/frontend/pages/api/workspace/renameWorkspace.js index b92addd1a..6dd297f82 100644 --- a/frontend/pages/api/workspace/renameWorkspace.js +++ b/frontend/pages/api/workspace/renameWorkspace.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -9,7 +8,7 @@ import SecurityClient from "~/utilities/SecurityClient"; */ const renameWorkspace = (workspaceId, newWorkspaceName) => { return SecurityClient.fetchCall( - PATH + "/api/v1/workspace/" + workspaceId + "/name", + "/api/v1/workspace/" + workspaceId + "/name", { method: "POST", headers: { diff --git a/frontend/pages/api/workspace/uploadKeys.js b/frontend/pages/api/workspace/uploadKeys.js index a368948aa..37b384f30 100644 --- a/frontend/pages/api/workspace/uploadKeys.js +++ b/frontend/pages/api/workspace/uploadKeys.js @@ -1,4 +1,3 @@ -import { PATH } from "~/const"; import SecurityClient from "~/utilities/SecurityClient"; /** @@ -10,7 +9,7 @@ import SecurityClient from "~/utilities/SecurityClient"; * @returns */ const uploadKeys = (workspaceId, userId, encryptedKey, nonce) => { - return SecurityClient.fetchCall(PATH + "/api/v1/key/" + workspaceId, { + return SecurityClient.fetchCall("/api/v1/key/" + workspaceId, { method: "POST", headers: { "Content-Type": "application/json", diff --git a/frontend/pages/dashboard/[id].js b/frontend/pages/dashboard/[id].js index b4cc2f728..a4dc9c782 100644 --- a/frontend/pages/dashboard/[id].js +++ b/frontend/pages/dashboard/[id].js @@ -35,19 +35,13 @@ import pushKeys from "~/components/utilities/secrets/pushKeys"; import pushKeysIntegration from "~/components/utilities/secrets/pushKeysIntegration"; import guidGenerator from "~/utilities/randomId"; +import { envMapping } from "../../public/data/frequentConstants"; import getWorkspaceIntegrations from "../api/integrations/getWorkspaceIntegrations"; import getUser from "../api/user/getUser"; import checkUserAction from "../api/userActions/checkUserAction"; import registerUserAction from "../api/userActions/registerUserAction"; import getWorkspaces from "../api/workspace/getWorkspaces"; -const envMapping = { - Development: "dev", - Staging: "staging", - Production: "prod", - Testing: "test", -}; - /** * This component represent a single row for an environemnt variable on the dashboard * @param {object} obj @@ -57,6 +51,7 @@ const envMapping = { * @param {function} obj.modifyValue - modify the value of a certain environment variable * @param {function} obj.modifyVisibility - switch between public/private visibility * @param {boolean} obj.isBlurred - if the blurring setting is turned on + * @param {string[]} obj.duplicates - list of all the duplicates secret names on the dashboard * @returns */ const KeyPair = ({ @@ -66,20 +61,22 @@ const KeyPair = ({ modifyValue, modifyVisibility, isBlurred, + duplicates }) => { const [randomStringLength, setRandomStringLength] = useState(32); const { t } = useTranslation(); return (
-
+
-
+
@@ -114,7 +111,7 @@ const KeyPair = ({ leaveFrom="transform opacity-100 scale-100" leaveTo="transform opacity-0 scale-95" > - +
modifyVisibility( @@ -378,37 +375,47 @@ export default function Dashboard() { ...data.map((row) => ({ [row[2]]: [row[3], row[4]] })) ); - // Once "Save changed is clicked", disable that button - setButtonReady(false); - pushKeys(obj, router.query.id, env); + // Checking if any of the secret keys start with a number - if so, don't do anything + const nameErrors = !Object.keys(obj).map(key => !isNaN(key.charAt(0))).every(v => v === false); + const duplicatesExist = data?.map(item => item[2]).filter((item, index) => index !== data?.map(item => item[2]).indexOf(item)).length > 0; - /** - * Check which integrations are active for this project and environment - * If there are any, update environment variables for those integrations - */ - let integrations = await getWorkspaceIntegrations({ - workspaceId: router.query.id, - }); - integrations.map(async (integration) => { - if ( - envMapping[env] == integration.environment && - integration.isActive == true - ) { - let objIntegration = Object.assign( - {}, - ...data.map((row) => ({ [row[2]]: row[3] })) - ); - await pushKeysIntegration({ - obj: objIntegration, - integrationId: integration._id, - }); + if (nameErrors) { + console.log("Solve all name errors first!"); + } else if (duplicatesExist) { + console.log("Remove the duplicated entries first!"); + } else { + // Once "Save changes is clicked", disable that button + setButtonReady(false); + pushKeys({obj, workspaceId: router.query.id, env}); + + /** + * Check which integrations are active for this project and environment + * If there are any, update environment variables for those integrations + */ + let integrations = await getWorkspaceIntegrations({ + workspaceId: router.query.id, + }); + integrations.map(async (integration) => { + if ( + envMapping[env] == integration.environment && + integration.isActive == true + ) { + let objIntegration = Object.assign( + {}, + ...data.map((row) => ({ [row[2]]: row[3] })) + ); + await pushKeysIntegration({ + obj: objIntegration, + integrationId: integration._id, + }); + } + }); + + // If this user has never saved environment variables before, show them a prompt to read docs + if (!hasUserEverPushed) { + setCheckDocsPopUpVisible(true); + await registerUserAction({ action: "first_time_secrets_pushed" }); } - }); - - // If this user has never saved environment variables before, show them a prompt to read docs - if (!hasUserEverPushed) { - setCheckDocsPopUpVisible(true); - await registerUserAction({ action: "first_time_secrets_pushed" }); } }; @@ -647,6 +654,7 @@ export default function Dashboard() { modifyKey={listenChangeKey} modifyVisibility={listenChangeVisibility} isBlurred={blurred} + duplicates={data?.map(item => item[2]).filter((item, index) => index !== data?.map(item => item[2]).indexOf(item))} /> ))}
@@ -694,6 +702,7 @@ export default function Dashboard() { modifyKey={listenChangeKey} modifyVisibility={listenChangeVisibility} isBlurred={blurred} + duplicates={data?.map(item => item[2]).filter((item, index) => index !== data?.map(item => item[2]).indexOf(item))} /> ))}
diff --git a/frontend/pages/home/[id].tsx b/frontend/pages/home/[id].tsx new file mode 100644 index 000000000..21f15fcc3 --- /dev/null +++ b/frontend/pages/home/[id].tsx @@ -0,0 +1,142 @@ +import React, { useEffect, useState } from "react"; +import Link from "next/link"; +import { useRouter } from "next/router"; +import { IconProp } from "@fortawesome/fontawesome-svg-core"; +import { faSlack } from "@fortawesome/free-brands-svg-icons"; +import { faCheckCircle, faHandPeace, faNetworkWired, faPlug, faPlus, faStar, faUserPlus } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +import getOrganizationUsers from "../api/organization/GetOrgUsers"; +import checkUserAction from "../api/userActions/checkUserAction"; +import registerUserAction from "../api/userActions/registerUserAction"; + +type ItemProps = { + text: string; + subText: string; + complete: boolean; + icon: IconProp; + time: string; + userAction?: string; + link?: string; +}; + +const learningItem = ({ text, subText, complete, icon, time, userAction, link }: ItemProps): JSX.Element => { + if (link) { + return ( + +
+
{ + if (userAction) { + await registerUserAction({ + action: userAction + }) + } + }} + className="relative bg-bunker-700 hover:bg-bunker-500 shadow-xl duration-200 rounded-md border border-dashed border-bunker-400 pl-2 pr-6 py-2 h-[5.5rem] w-full flex items-center justify-between overflow-hidden my-1.5 cursor-pointer"> +
+ + {complete && +
+ +
} +
+
{text}
+
{subText}
+
+
+
+ {complete ? "Complete!" : "About " + time} +
+ {complete &&
} +
+
+ + ); + } else { + return ( +
{ + if (userAction) { + await registerUserAction({ + action: userAction + }) + } + }} + className="relative bg-bunker-700 hover:bg-bunker-500 shadow-xl duration-200 rounded-md border border-dashed border-bunker-400 pl-2 pr-6 py-2 h-[5.5rem] w-full flex items-center justify-between overflow-hidden my-1.5 cursor-pointer"> +
+ + {complete && +
+ +
} +
+
{text}
+
{subText}
+
+
+
+ {complete ? "Complete!" : "About " + time} +
+ {complete &&
} +
+ ); + } +} + +/** + * This tab is called Home because in the future it will include some company news, + * updates, roadmap, relavant blogs, etc. Currently it only has the setup instruction + * for the new users + */ +export default function Home() { + const router = useRouter(); + const [hasUserClickedSlack, setHasUserClickedSlack] = useState(false); + const [hasUserClickedIntro, setHasUserClickedIntro] = useState(false); + const [hasUserStarred, setHasUserStarred] = useState(false); + const [usersInOrg, setUsersInOrg] = useState(false); + + useEffect(() => { + const checkUserActionsFunction = async () => { + const userActionSlack = await checkUserAction({ + action: "slack_cta_clicked", + }); + setHasUserClickedSlack(userActionSlack ? true : false); + + const userActionIntro = await checkUserAction({ + action: "intro_cta_clicked", + }); + setHasUserClickedIntro(userActionIntro ? true : false); + + const userActionStar = await checkUserAction({ + action: "star_cta_clicked", + }); + setHasUserStarred(userActionStar ? true : false); + + const orgId = localStorage.getItem("orgData.id"); + const orgUsers = await getOrganizationUsers({ + orgId: orgId ? orgId : "", + }); + setUsersInOrg(orgUsers.length > 1) + }; + checkUserActionsFunction(); + }, []); + + return ( +
+
+
Your quick start guide
+
Click on the items below and follow the instructions.
+ {learningItem({ text: "Get to know Infisical", subText: "", complete: hasUserClickedIntro, icon: faHandPeace, time: "3 min", userAction: "intro_cta_clicked", link: "https://www.youtube.com/watch?v=JS3OKYU2078" })} + {learningItem({ text: "Add your secrets", subText: "Click to see example secrets, and add your own.", complete: false, icon: faPlus, time: "2 min", userAction: "first_time_secrets_pushed", link: "/dashboard/" + router.query.id })} + {learningItem({ text: "Inject secrets locally", subText: "Replace .env files with a more secure an efficient alternative.", complete: false, icon: faNetworkWired, time: "8 min", link: "https://infisical.com/docs/getting-started/quickstart" })} + {learningItem({ text: "Integrate Infisical with your infrastructure", subText: "Only a few integrations are currently available. Many more coming soon!", complete: false, icon: faPlug, time: "15 min", link: "https://infisical.com/docs/integrations/overview" })} + {learningItem({ text: "Invite your teammates", subText: "", complete: usersInOrg, icon: faUserPlus, time: "2 min", link: "/settings/org/" + router.query.id + "?invite" })} + {learningItem({ text: "Join Infisical Slack", subText: "Have any questions? Ask us!", complete: hasUserClickedSlack, icon: faSlack, time: "1 min", userAction: "slack_cta_clicked", link: "https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g" })} + {learningItem({ text: "Star Infisical on GitHub", subText: "Like what we're doing? You know what to do! :)", complete: hasUserStarred, icon: faStar, time: "1 min", userAction: "star_cta_clicked", link: "https://github.com/Infisical/infisical" })} +
+
+ ); +} + +Home.requireAuth = true; diff --git a/frontend/pages/integrations/[id].js b/frontend/pages/integrations/[id].js index aa9dd8412..264475ec2 100644 --- a/frontend/pages/integrations/[id].js +++ b/frontend/pages/integrations/[id].js @@ -18,6 +18,11 @@ import getSecretsForProject from "~/components/utilities/secrets/getSecretsForPr import pushKeysIntegration from "~/components/utilities/secrets/pushKeysIntegration"; import guidGenerator from "~/utilities/randomId"; +import { + envMapping, + frameworks, + reverseEnvMapping, +} from "../../public/data/frequentConstants"; import deleteIntegration from "../api/integrations/DeleteIntegration"; import deleteIntegrationAuth from "../api/integrations/DeleteIntegrationAuth"; import getIntegrationApps from "../api/integrations/GetIntegrationApps"; @@ -28,20 +33,6 @@ import startIntegration from "../api/integrations/StartIntegration"; const crypto = require("crypto"); -const envMapping = { - Development: "dev", - Staging: "staging", - Production: "prod", - Testing: "test", -}; - -const reverseEnvMapping = { - dev: "Development", - staging: "Staging", - prod: "Production", - test: "Testing", -}; - const Integration = ({ projectIntegration }) => { const [integrationEnvironment, setIntegrationEnvironment] = useState( reverseEnvMapping[projectIntegration.environment] @@ -262,7 +253,7 @@ export default function Integrations() { {t("integrations:available-text2")}

-
+
{Object.keys(integrations).map((integration) => (
integration logo {integrations[integration].name.split(" ").length > 2 ? ( -
+
{integrations[integration].name.split(" ")[0]}
{integrations[integration].name.split(" ")[1]}{" "} @@ -300,7 +291,7 @@ export default function Integrations() {
) : ( -
+
{integrations[integration].name}
)} @@ -350,6 +341,46 @@ export default function Integrations() {
))}
+
+
+

Framework Integrations

+
+

+ Click on a framework to get the setup instructions. +

+
+
diff --git a/frontend/pages/settings/billing/[id].js b/frontend/pages/settings/billing/[id].js index f32d56147..9108efbe0 100644 --- a/frontend/pages/settings/billing/[id].js +++ b/frontend/pages/settings/billing/[id].js @@ -54,7 +54,7 @@ export default function SettingsBilling() { orgId: localStorage.getItem("orgData.id"), }); - setCurrentPlan(subscriptions.data[0].plan.product); + setCurrentPlan(subscriptions.data[0].plan.id); const orgUsers = await getOrganizationUsers({ orgId: localStorage.getItem("orgData.id"), }); diff --git a/frontend/pages/signup.js b/frontend/pages/signup.js index 6c3d0b0f0..06632014e 100644 --- a/frontend/pages/signup.js +++ b/frontend/pages/signup.js @@ -201,7 +201,7 @@ export default function SignUp() { }, async () => { client.createVerifier(async (err, result) => { - const response = await completeAccountInformationSignup({ + let response = await completeAccountInformationSignup({ email, firstName, lastName, @@ -473,7 +473,7 @@ export default function SignUp() {
-
+