Merge remote-tracking branch 'upstream/main' into feat/39

This commit is contained in:
이강준
2022-12-06 22:07:29 +09:00
163 changed files with 2639 additions and 1327 deletions
+7 -4
View File
@@ -6,6 +6,8 @@ jobs:
docker: docker:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout
uses: actions/checkout@v2
- -
name: Set up QEMU name: Set up QEMU
uses: docker/setup-qemu-action@v2 uses: docker/setup-qemu-action@v2
@@ -23,12 +25,13 @@ jobs:
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
with: with:
push: true push: true
context: /backend context: backend
tags: infisical/backend:latest tags: infisical/backend:test
- -
name: Build and push frontend name: Build and push frontend
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
with: with:
push: true push: true
context: /frontend file: frontend/Dockerfile.dev
tags: infisical/frontend:latest context: frontend
tags: infisical/frontend:test
+22
View File
@@ -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 }}
+1 -1
View File
@@ -1,4 +1,4 @@
name: goreleaser name: Go releaser
on: on:
push: push:
+182 -24
View File
@@ -8,7 +8,7 @@
<h4 align="center"> <h4 align="center">
<a href="https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g">Slack</a> | <a href="https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g">Slack</a> |
<a href="https://infisical.com/signup">Infisical Cloud</a> | <a href="https://infisical.com/">Infisical Cloud</a> |
<a href="https://infisical.com/docs/self-hosting/overview">Self-Hosting</a> | <a href="https://infisical.com/docs/self-hosting/overview">Self-Hosting</a> |
<a href="https://infisical.com/docs/getting-started/introduction">Docs</a> | <a href="https://infisical.com/docs/getting-started/introduction">Docs</a> |
<a href="https://www.infisical.com">Website</a> <a href="https://www.infisical.com">Website</a>
@@ -21,6 +21,9 @@
<a href="https://github.com/infisical/infisical/blob/main/CONTRIBUTING.md"> <a href="https://github.com/infisical/infisical/blob/main/CONTRIBUTING.md">
<img src="https://img.shields.io/badge/PRs-Welcome-brightgreen" alt="PRs welcome!" /> <img src="https://img.shields.io/badge/PRs-Welcome-brightgreen" alt="PRs welcome!" />
</a> </a>
<a href="">
<img src="https://img.shields.io/github/commit-activity/m/infisical/infisical" alt="git commit activity" />
</a>
<a href="https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g"> <a href="https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g">
<img src="https://img.shields.io/badge/chat-on%20Slack-blueviolet" alt="Slack community channel" /> <img src="https://img.shields.io/badge/chat-on%20Slack-blueviolet" alt="Slack community channel" />
</a> </a>
@@ -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. **[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 - **[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 - **[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.) - **Navigate Multiple Environments** per project (e.g. development, staging, production, etc.)
- **Personal/Shared** scoping for environment variables - **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 - 🔜 **1-Click Deploy** to Digital Ocean and Heroku
- 🔜 **Authentication/Authorization** for projects (read/write controls soon) - 🔜 **Authentication/Authorization** for projects (read/write controls soon)
- 🔜 **Automatic Secret Rotation** - 🔜 **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? ## 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, <i>not just security teams</i>.
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. 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. 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 ## Contributing
@@ -69,9 +70,10 @@ Not sure where to get started? [Book a free, non-pressure pairing sessions with
## Community & Support ## Community & Support
- [GitHub Discussions](https://github.com/Infisical/infisical/discussions) for help with building and discussion. - [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 Issues](https://github.com/Infisical/infisical-cli/issues) for any bugs and errors you encounter using Infisical. - [GitHub Discussions](https://github.com/Infisical/infisical/discussions) (For help with building and deeper conversations about features)
- [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. - [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 ## Status
@@ -89,23 +91,179 @@ Infisical officially launched as v.1.0 on November 21st, 2022. However, a lot of
## Integrations ## 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! :)
<table>
<tr>
<th>Platforms </th>
<th>Frameworks</th>
</tr>
<tr>
<td>
<table>
<tbody>
<tr>
<td align="left" valign="middle">
<a href="https://infisical.com/docs/integrations/platforms/docker?ref=github.com">
✔️ Docker
</a>
</td>
<td align="left" valign="middle">
<a href="https://infisical.com/docs/integrations/platforms/docker-compose?ref=github.com">
✔️ Docker Compose
</a>
</td>
<td align="left" valign="middle">
<a href="https://infisical.com/docs/integrations/cloud/heroku?ref=github.com">
✔️ Heroku
</a>
</td>
</tr>
<tr>
<td align="left" valign="middle">
🔜 Vercel (https://github.com/Infisical/infisical/issues/60)
</td>
<td align="left" valign="middle">
🔜 GitLab CI/CD
</td>
<td align="left" valign="middle">
🔜 Fly.io
</td>
</tr>
<tr>
<td align="left" valign="middle">
🔜 AWS
</td>
<td align="left" valign="middle">
🔜 GitHub Actions (https://github.com/Infisical/infisical/issues/54)
</td>
<td align="left" valign="middle">
🔜 Railway
</td>
</tr>
<tr>
<td align="left" valign="middle">
🔜 GCP
</td>
<td align="left" valign="middle">
🔜 Kubernetes
</td>
<td align="left" valign="middle">
🔜 CircleCI
</td>
</tr>
<tr>
<td align="left" valign="middle">
🔜 Jenkins
</td>
<td align="left" valign="middle">
🔜 Digital Ocean
</td>
<td align="left" valign="middle">
🔜 Azure
</td>
</tr>
<tr>
<td align="left" valign="middle">
🔜 TravisCI
</td>
<td align="left" valign="middle">
🔜 Netlify (https://github.com/Infisical/infisical/issues/55)
</td>
</tr>
</tbody>
</table>
</td>
<td>
<table>
<tbody>
<tr>
<td align="left" valign="middle">
<a href="https://infisical.com/docs/integrations/frameworks/react?ref=github.com">
✔️ React
</a>
</td>
<td align="left" valign="middle">
<a href="https://infisical.com/docs/integrations/frameworks/express?ref=github.com">
✔️ Express
</a>
</td>
</tr>
<tr>
<td align="left" valign="middle">
<a href="https://infisical.com/docs/integrations/frameworks/gatsby?ref=github.com">
✔️ Gatsby
</a>
</td>
<td align="left" valign="middle">
<a href="https://infisical.com/docs/integrations/frameworks/flask?ref=github.com">
✔️ Flask
</a>
</td>
</tr>
<tr>
<td align="left" valign="middle">
<a href="https://infisical.com/docs/integrations/frameworks/django?ref=github.com">
✔️ Django
</a>
</td>
<td align="left" valign="middle">
<a href="https://infisical.com/docs/integrations/frameworks/laravel?ref=github.com">
✔️ Laravel
</a>
</td>
</tr>
<tr>
<td align="left" valign="middle">
<a href="https://infisical.com/docs/integrations/frameworks/nestjs?ref=github.com">
✔️ NestJS
</a>
</td>
<td align="left" valign="middle">
<a href="https://infisical.com/docs/integrations/frameworks/remix?ref=github.com">
✔️ Remix
</a>
</td>
</tr>
<tr>
<td align="left" valign="middle">
<a href="https://infisical.com/docs/integrations/frameworks/nextjs?ref=github.com">
✔️ Next.js
</a>
</td>
<td align="left" valign="middle">
<a href="https://infisical.com/docs/integrations/frameworks/vite?ref=github.com">
✔️ Vite
</a>
</td>
</tr>
<tr>
<td align="left" valign="middle">
<a href="https://infisical.com/docs/integrations/frameworks/rails?ref=github.com">
✔️ Ruby on Rails
</a>
</td>
<td align="left" valign="middle">
<a href="https://infisical.com/docs/integrations/frameworks/vue?ref=github.com">
✔️ Vue
</a>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
- [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 ## 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 ## Security
@@ -119,4 +277,4 @@ Looking to report a security vulnerability? Please don't post about it in GitHub
<!-- prettier-ignore-start --> <!-- prettier-ignore-start -->
<!-- markdownlint-disable --> <!-- markdownlint-disable -->
<a href="https://github.com/dangtony98"><img src="https://avatars.githubusercontent.com/u/25857006?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/mv-turtle"><img src="https://avatars.githubusercontent.com/u/78047717?s=96&v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/maidul98"><img src="https://avatars.githubusercontent.com/u/9300960?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/hanywang2"><img src="https://avatars.githubusercontent.com/u/44352119?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/tobias-mintlify"><img src="https://avatars.githubusercontent.com/u/110702161?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/gangjun06"><img src="https://avatars.githubusercontent.com/u/50910815?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/dangtony98"><img src="https://avatars.githubusercontent.com/u/25857006?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/mv-turtle"><img src="https://avatars.githubusercontent.com/u/78047717?s=96&v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/maidul98"><img src="https://avatars.githubusercontent.com/u/9300960?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/gangjun06"><img src="https://avatars.githubusercontent.com/u/50910815?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/reginaldbondoc"><img src="https://avatars.githubusercontent.com/u/7693108?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/SH5H"><img src="https://avatars.githubusercontent.com/u/25437192?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/hanywang2"><img src="https://avatars.githubusercontent.com/u/44352119?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/tobias-mintlify"><img src="https://avatars.githubusercontent.com/u/110702161?v=4" width="50" height="50" alt=""/></a> <a href="https://github.com/0xflotus"><img src="https://avatars.githubusercontent.com/u/26602940?v=4" width="50" height="50" alt=""/></a>
-2
View File
@@ -22,8 +22,6 @@
"query-string": "^7.1.1", "query-string": "^7.1.1",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"stripe": "^10.7.0", "stripe": "^10.7.0",
"swagger-jsdoc": "^6.2.5",
"swagger-ui-express": "^4.6.0",
"tweetnacl": "^1.0.3", "tweetnacl": "^1.0.3",
"tweetnacl-util": "^0.15.1", "tweetnacl-util": "^0.15.1",
"typescript": "^4.8.4" "typescript": "^4.8.4"
+1 -1
View File
@@ -106,7 +106,7 @@ const initializeDefaultOrg = async ({
// initialize a default workspace inside the new organization // initialize a default workspace inside the new organization
const workspace = await createWorkspace({ const workspace = await createWorkspace({
name: `${user.firstName}'s Project`, name: `Example Project`,
organizationId: organization._id.toString() organizationId: organization._id.toString()
}); });
-35
View File
@@ -4,8 +4,6 @@ import cors from 'cors';
import cookieParser from 'cookie-parser'; import cookieParser from 'cookie-parser';
import mongoose from 'mongoose'; import mongoose from 'mongoose';
import dotenv from 'dotenv'; import dotenv from 'dotenv';
import swaggerJSDoc from 'swagger-jsdoc';
import swaggerUi from 'swagger-ui-express';
dotenv.config(); dotenv.config();
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
@@ -68,39 +66,6 @@ if (NODE_ENV === 'production') {
app.use(helmet()); 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()); app.use(express.json());
// routers // routers
-47
View File
@@ -11,53 +11,6 @@ router.post(
authController.getNewToken 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: '[email protected]'
* 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( router.post(
'/login1', '/login1',
loginLimiter, loginLimiter,
+1 -1
View File
@@ -15,7 +15,7 @@ var rootCmd = &cobra.Command{
Short: "Infisical CLI is used to inject environment variables into any process", 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.`, 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}, 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. // Execute adds all child commands to the root command and sets flags appropriately.
+6
View File
@@ -95,3 +95,9 @@ The Infisical CLI provides a way to inject environment variables from the platfo
</Tab> </Tab>
</Tabs> </Tabs>
## Log in to the Infisical CLI
```bash
infisical login
```
+2 -2
View File
@@ -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: 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](../../integrations/platforms/docker)
- [Docker Compose](../../integrations/docker-compose) - [Docker Compose](../../integrations/platforms/docker-compose)
<Info> <Info>
Once the token is expired, the CLI using it will no longer be able to make Once the token is expired, the CLI using it will no longer be able to make
+5 -11
View File
@@ -4,12 +4,6 @@ title: "Usage"
Prerequisite: [Install the CLI](/cli/overview) Prerequisite: [Install the CLI](/cli/overview)
## Log in to the Infisical CLI
```bash
infisical login
```
## Initialize Infisical for your project ## Initialize Infisical for your project
```bash ```bash
@@ -29,11 +23,11 @@ infisical run -- [your application start command]
Options you can specify: Options you can specify:
| Option | Description | Default value | | Option | Description | Default value |
| ------------- | ----------------------------------------------------------------------------------------------------------- | ------------- | | ------------- | ----------------------------------------------------------------------------------------------------------- | ------------- |
| `--env` | Used to set the environment that secrets are pulled from. Accepted values: `dev`, `staging`, `test`, `prod` | `dev` | | `--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` | | `--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` | | `--expand` | Parse shell parameter expansions in your secrets (e.g., `${DOMAIN}`) | `true` |
## Examples: ## Examples:
+16
View File
@@ -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`.
+1 -1
View File
@@ -16,7 +16,7 @@ cd infisical
## Set up environment variables ## 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 ```bash
cp .env.example .env cp .env.example .env
@@ -8,24 +8,7 @@ If you're using a self-hosted installation, follow the [setup](/self-hosting/ove
## Infisical Cloud ## 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. Open [infisical.com](https://infisical.com/) and click on either "Try Infisical for free" or "Start for free" to complete 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 |
Once you've done that, you'll be taken to the dashboard where we've populated some default environment variables for demonstration. Once you've done that, you'll be taken to the dashboard where we've populated some default environment variables for demonstration.
@@ -2,12 +2,10 @@
title: "Integrations" 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) ![integrations](../../images/project-integrations.png)
Check out integrations: Check out our [integrations](/integrations/overview).
- [Heroku](/integrations/heroku)
- [Docker](/integrations/docker)
- [Docker Compose](/integrations/docker-compose)
@@ -2,15 +2,12 @@
title: "Organization" 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 name modal open](../../images/dashboard-name-modal-organization.png)
![organization name modal open](../../images/organization.png)
![organization page](../../images/organization.png)
Feel free to change the name of your organization.
## Members ## 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. 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](../../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>
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.
</Note>
## Incident contacts ## 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. 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](../../images/organization-ic.png)
![organization incident contacts add](../../images/organization-ic-add.png)
+1 -9
View File
@@ -2,7 +2,7 @@
title: "Project" title: "Project"
--- ---
A project houses environment variables for an application or service. A project houses environment variables for an application.
## Dashboard ## 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: 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 open](../../images/project-envar-toggle-open.png)
![project variable toggle moved](../../images/project-envar-toggle-moved.png)
### Search ### Search
You can search for any environment variable by its key. You can search for any environment variable by its key.
![project search](../../images/project-search.png) ![project search](../../images/project-search.png)
![project search typed](../../images/project-search-typed.png)
### Sort ### Sort
You can sort environment variables alphabetically by their keys. 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 hide](../../images/project-hide.png)
![project unhide](../../images/project-hide.png)
### Download as .env ### Download as .env
You can download your environment variables back in a .env file. You can download your environment variables back in a .env file.
+7 -12
View File
@@ -2,20 +2,15 @@
title: "Infisical Token" 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: It's possible to generate the token in the settings of a project.
- [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.
![token add](../../images/project-token-add.png) ![token add](../../images/project-token-add.png)
![token name](../../images/project-token-name.png) <Note>
The token grants read-only access to a particular environment and project for
![token added](../../images/project-token-added.png) a specified amount of time.
</Note>
+1 -1
View File
@@ -27,7 +27,7 @@ Start syncing environment variables with [Infisical Cloud](https://app.infisical
Learn how to configure and deploy Infisical. Learn how to configure and deploy Infisical.
</Card> </Card>
<Card <Card
href="/integrations/heroku" href="/integrations/overview"
title="Integrations" title="Integrations"
icon="plug" icon="plug"
color="#dc2626" color="#dc2626"
+24
View File
@@ -0,0 +1,24 @@
---
title: "Django"
---
Prerequisites:
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
- [Install the CLI](/cli/overview)
## Initialize Infisical for your [Django](https://www.djangoproject.com) project
```bash
# move to your Django project
cd /path/to/project
# initialize infisical
infisical init
```
## Start your server with environment variables injected
```bash
infisical run -- python manage.py runserver
```
+45
View File
@@ -0,0 +1,45 @@
---
title: "Express, Fastify, Koa"
---
Prerequisites:
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
- [Install the CLI](/cli/overview)
The steps apply to the following non-exhaustive list of frameworks:
- [Express](https://expressjs.com)
- [Fastify](https://www.fastify.io)
- [Koa](https://koajs.com)
## Initialize Infisical for your app
```bash
# move to your app
cd /path/to/project
# initialize infisical
infisical init
```
## Modify the start script in your `package.json`
```json
...
"scripts": {
"start": "infisical run -- node index.js"
"dev": "infisical run -- nodemon index.js" // if using nodemon for dev
}
...
```
## Start your server with environment variables injected
```bash
npm run start
# or start development server
npm run dev
```
+10
View File
@@ -0,0 +1,10 @@
---
title: "Fiber"
---
Prerequisites:
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
- [Install the CLI](/cli/overview)
Coming soon.
+24
View File
@@ -0,0 +1,24 @@
---
title: "Flask"
---
Prerequisites:
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
- [Install the CLI](/cli/overview)
## Initialize Infisical for your [Flask](https://flask.palletsprojects.com/en/2.2.x) app
```bash
# move to your Flask app
cd /path/to/project
# initialize infisical
infisical init
```
## Start your server with environment variables injected
```bash
infisical run -- flask run
```
+44
View File
@@ -0,0 +1,44 @@
---
title: "Gatsby"
---
Prerequisites:
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
- [Install the CLI](/cli/overview)
## Initialize Infisical for your [Gatsby](https://www.gatsbyjs.com) app
```bash
# move to your app
cd /path/to/project
# initialize infisical
infisical init
```
## Modify the start script in your `package.json`
```json
...
"scripts": {
"develop": "infisical run -- gatsby develop",
"start": "infisical run -- gatsby develop",
"build": "infisical run -- gatsby build",
"serve": "infisical run -- gatsby serve",
"clean": "infisical run -- gatsby clean"
}
...
```
## Start your development server with environment variables injected
```bash
npm run develop
```
<Note>
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).
</Note>
+10
View File
@@ -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.
+50
View File
@@ -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
```
+46
View File
@@ -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>
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).
</Note>
+36
View File
@@ -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
```
+24
View File
@@ -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
```
+37
View File
@@ -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
```
+41
View File
@@ -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
```
+43
View File
@@ -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>
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).
</Note>
+42
View File
@@ -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>
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).
</Note>
+31 -17
View File
@@ -2,23 +2,37 @@
title: "Overview" 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). Missing an integration? Throw in a [request](https://github.com/Infisical/infisical/issues).
| Integration | Status | | Integration | Type | Status |
| ---------------------------------------------- | ----------- | | -------------------------------------------------------- | --------- | ----------- |
| [Docker](/integrations/docker) | Available | | [Docker](/integrations/platforms/docker) | Platform | Available |
| [Docker-Compose](/integrations/docker-compose) | Available | | [Docker-Compose](/integrations/platforms/docker-compose) | Platform | Available |
| [Heroku](/integrations/heroku) | Available | | Kubernetes | Platform | Coming soon |
| Kubernetes | Coming soon | | [Heroku](/integrations/cloud/heroku) | Cloud | Available |
| Vercel | Coming soon | | Vercel | Cloud | Coming soon |
| AWS | Coming soon | | AWS | Cloud | Coming soon |
| GCP | Coming soon | | GCP | Cloud | Coming soon |
| Azure | Coming soon | | Azure | Cloud | Coming soon |
| DigitalOcean | Coming soon | | DigitalOcean | Cloud | Coming soon |
| GitLab | Coming soon | | GitLab | CI/CD | Coming soon |
| CircleCI | Coming soon | | CircleCI | CI/CD | Coming soon |
| TravisCI | Coming soon | | TravisCI | CI/CD | Coming soon |
| GitHub Actions | Coming soon | | GitHub Actions | CI/CD | Coming soon |
| Jenkins | 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 |
@@ -2,11 +2,13 @@
title: "Docker Compose" 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. 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. 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. have to generate a Infisical Token for each service.
</Info> </Info>
### 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. 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. This will ensure that you can set Infisical Tokens for multiple services.
@@ -41,7 +43,7 @@ services:
- INFISICAL_TOKEN=${INFISICAL_TOKEN_FOR_API} - 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` 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. in your Docker Compose file.
@@ -2,7 +2,9 @@
title: "Docker" 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
<Tabs> <Tabs>
<Tab title="Alpine"> <Tab title="Alpine">
@@ -29,14 +31,10 @@ title: "Docker"
</Tab> </Tab>
</Tabs> </Tabs>
## Step 2: Generate Infisical Token ## Modify the start command in your Dockerfile
[Generate an Infisical Token](../../getting-started/dashboard/token) and keep it handy.
## Step 3: Set start command of your container
```dockerfile ```dockerfile
CMD ["infisical", "--env=[your-project-env-name]", "projectId=[your-project-id]", "run", "---", "<your application start command>"] CMD ["infisical", "--env=[env]", "projectId=[projectId]", "run", "---", "[your application start command]"]
# example # example
CMD ["infisical", "--env=prod", "projectId=62faf98ae0b05e83239b5da41", "run", "---", "npm run start"] 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` | | `--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` | | `--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 ```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.
+127 -70
View File
@@ -27,90 +27,147 @@
"url": "https://app.infisical.com/signup" "url": "https://app.infisical.com/signup"
}, },
"anchors": [ "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", "name": "Blog",
"icon": "newspaper", "icon": "newspaper",
"url": "https://blog.infisical.com/" "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": [ "navigation": [
{ {
"group": " ", "group": "Overview",
"pages": [ "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": [ "pages": [
"getting-started/introduction", "cli/reference/commands",
"getting-started/quickstart", "cli/reference/login",
"getting-started/features" "cli/reference/init",
] "cli/reference/run"
},
{
"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"
] ]
} }
] ]
},
{
"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" "backgroundImage": "/images/background.png"
@@ -0,0 +1,54 @@
---
title: "Kubernetes"
description: "Deploy with Kubernetes"
---
<Info>
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.
</Info>
**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 <namespace-to-install-to>` to your `helm install` command.
```bash
## Installs to default namespace
helm install infisical-helm-charts/infisical --values <path to the values.yaml you downloaded/created in step 2>
```
<Note>
If you have not filled out all of the required environment variables, you will see an error message prompting you to
do so.
</Note>
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.
+1 -1
View File
@@ -51,4 +51,4 @@ nano .env
docker-compose -f docker-compose.yml up -d 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.
+10 -5
View File
@@ -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. 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.
</Info> </Info>
## Deployment options ## 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: <CardGroup cols={2}>
<Card title="Any Linux" icon="square-1" color="#ea5a0c" href="/self-hosting/deployments/linux">
- [Linux VM](/self-hosting/deployments/linux) Deploy to any Linux with Docker
</Card>
<Card title="Kubernetes" icon="square-2" color="#0285c7" href="/self-hosting/deployments/kubernetes">
Deploy to your Kubernetes cluster
</Card>
</CardGroup>
## Telemetry ## Telemetry
+18 -15
View File
@@ -1,10 +1,21 @@
{ {
"extends": "next/core-web-vitals", "extends": [
"plugins": [ "eslint:recommended",
"simple-import-sort" "plugin:@typescript-eslint/recommended",
"next",
"next/core-web-vitals"
], ],
"parser": "@typescript-eslint/parser",
"plugins": ["simple-import-sort", "@typescript-eslint"],
"rules": { "rules": {
"react-hooks/exhaustive-deps": "off", "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/exports": "warn",
"simple-import-sort/imports": [ "simple-import-sort/imports": [
"warn", "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)(/.*|$)" "^(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 // Packages `react` related packages
[ ["^react", "^next", "^@?\\w"],
"^react",
"^next",
"^@?\\w"
],
// Internal packages. // Internal packages.
[ ["^~(/.*|$)"],
"^~(/.*|$)"
],
// Relative imports // Relative imports
[ [
"^\\.\\.(?!/?$)", "^\\.\\.(?!/?$)",
@@ -36,11 +41,9 @@
"^\\./?$" "^\\./?$"
], ],
// Style imports. // Style imports.
[ ["^.+\\.?(css|scss)$"]
"^.+\\.?(css|scss)$"
]
] ]
} }
] ]
} }
} }
+10 -8
View File
@@ -1,20 +1,22 @@
This is the client repository for Infisical. 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. Please ensure you have Docker and Docker Compose installed for your OS.
### Steps to start server ### Steps to start server
- `CD` into the repo - `CD` into the repo
- run command `docker-compose -f docker-compose.dev.yml up --build --force-recreate` - 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 - `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 ### 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 You will also need to ensure that a .env.local file exists with all required environment variables
@@ -1,8 +1,8 @@
import React, { useState } from "react"; import React from "react";
import { faExclamationTriangle } from "@fortawesome/free-solid-svg-icons"; import { faExclamationTriangle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
export default function Error({ text }) { export default function Error({ text }: { text: string }): JSX.Element {
return ( return (
<div className="relative bg-red-500 opacity-100 border flex flex-row justify-center m-auto items-center w-fit rounded-full mb-4"> <div className="relative bg-red-500 opacity-100 border flex flex-row justify-center m-auto items-center w-fit rounded-full mb-4">
<FontAwesomeIcon <FontAwesomeIcon
@@ -1,6 +1,5 @@
import React from "react"; import React from "react";
import { Fragment } from "react"; import { Fragment } from "react";
import { useRouter } from "next/router";
import { import {
faAngleDown, faAngleDown,
faCheck, faCheck,
@@ -9,9 +8,25 @@ import {
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Listbox, Transition } from "@headlessui/react"; import { Listbox, Transition } from "@headlessui/react";
type ListBoxProps = {
selected: string,
onChange: () => void,
data: string[],
text: string,
buttonAction: () => void,
width: string,
}
/** /**
* This is the component that we use for drop down lists. * 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 * @returns
*/ */
export default function ListBox({ export default function ListBox({
@@ -21,10 +36,7 @@ export default function ListBox({
text, text,
buttonAction, buttonAction,
width, width,
workspaceMapping = [], } : ListBoxProps): JSX.Element {
}) {
const router = useRouter();
return ( return (
<Listbox value={selected} onChange={onChange}> <Listbox value={selected} onChange={onChange}>
<div className="relative"> <div className="relative">
@@ -61,7 +73,7 @@ export default function ListBox({
`my-0.5 relative cursor-default select-none py-2 pl-10 pr-4 rounded-md ${ `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" : "" selected ? "bg-white/10 text-gray-400 font-bold" : ""
} ${ } ${
active & !selected active && !selected
? "bg-white/5 text-mineshaft-200 cursor-pointer" ? "bg-white/5 text-mineshaft-200 cursor-pointer"
: "text-gray-400" : "text-gray-400"
} ` } `
+46 -20
View File
@@ -1,35 +1,59 @@
import React from "react"; import React from "react";
import Image from "next/image"; import Image from "next/image";
import { IconProp } from "@fortawesome/fontawesome-svg-core"; 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 = { type ButtonProps = {
text: string; text: string;
onButtonPressed: () => void; onButtonPressed: () => void;
loading: boolean; loading?: boolean;
color: string; color: string;
size: string; size: string;
icon: IconProp; icon?: IconProp;
active: boolean; active?: boolean;
iconDisabled: string; iconDisabled?: string;
textDisabled: 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; // Check if the button show always be 'active' - then true;
// or if it should switch between 'active' and 'disabled' - then give the status // or if it should switch between 'active' and 'disabled' - then give the status
const activityStatus = props.active || (props.text != "" && props.textDisabled == undefined) const activityStatus =
props.active || (props.text != "" && props.textDisabled == undefined);
let styleButton = classNames(
const styleButton = classNames(
"group m-auto md:m-0 inline-block rounded-md duration-200", "group m-auto md:m-0 inline-block rounded-md duration-200",
// Setting background colors and hover modes // 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 == "mineshaft" && !activityStatus && "bg-mineshaft",
(props.color == "primary" || !props.color) && activityStatus && "bg-primary hover:opacity-80", (props.color == "primary" || !props.color) &&
(props.color == "primary" || !props.color) && !activityStatus && "bg-primary", activityStatus &&
"bg-primary hover:opacity-80",
(props.color == "primary" || !props.color) &&
!activityStatus &&
"bg-primary",
props.color == "red" && "bg-red", props.color == "red" && "bg-red",
// Changing the opacity when active vs when not // 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" 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", "relative font-medium flex items-center",
// Setting the text color for the text and icon // 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" props.size == "icon" && "flex items-center justify-center"
); );
let textStyle = classNames( const textStyle = classNames(
"relative duration-200", "relative duration-200",
// Show the loading sign if the loading indicator is on // 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 == "md" && "text-sm",
props.size == "lg" && "text-lg" props.size == "lg" && "text-lg"
); );
@@ -86,7 +110,7 @@ export default function Button (props: ButtonProps): JSX.Element {
</div> </div>
{props.icon && ( {props.icon && (
<FontAwesomeIcon <FontAwesomeIcon
icon={props.icon as IconProp} icon={props.icon}
className={`flex my-auto font-extrabold ${ className={`flex my-auto font-extrabold ${
props.size == "icon-sm" ? "text-sm" : "text-md" props.size == "icon-sm" ? "text-sm" : "text-md"
} ${(props.text || props.textDisabled) && "mr-2"}`} } ${(props.text || props.textDisabled) && "mr-2"}`}
@@ -101,7 +125,9 @@ export default function Button (props: ButtonProps): JSX.Element {
/> />
)} )}
{(props.text || props.textDisabled) && ( {(props.text || props.textDisabled) && (
<p className={textStyle}>{activityStatus ? props.text : props.textDisabled}</p> <p className={textStyle}>
{activityStatus ? props.text : props.textDisabled}
</p>
)} )}
</div> </div>
</button> </button>
@@ -9,6 +9,7 @@ import nacl from "tweetnacl";
import addServiceToken from "~/pages/api/serviceToken/addServiceToken"; import addServiceToken from "~/pages/api/serviceToken/addServiceToken";
import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey"; import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey";
import { envMapping } from "../../../public/data/frequentConstants";
import { import {
decryptAssymmetric, decryptAssymmetric,
encryptAssymmetric, encryptAssymmetric,
@@ -17,13 +18,6 @@ import Button from "../buttons/Button";
import InputField from "../InputField"; import InputField from "../InputField";
import ListBox from "../Listbox"; import ListBox from "../Listbox";
const envMapping = {
Development: "dev",
Staging: "staging",
Production: "prod",
Testing: "test",
};
const expiryMapping = { const expiryMapping = {
"1 day": 86400, "1 day": 86400,
"7 days": 604800, "7 days": 604800,
@@ -45,7 +39,7 @@ const AddServiceTokenDialog = ({
const { t } = useTranslate(); const { t } = useTranslate();
const generateServiceToken = async () => { const generateServiceToken = async () => {
const latestFileKey = await getLatestFileKey(workspaceId); const latestFileKey = await getLatestFileKey({ workspaceId });
const key = decryptAssymmetric({ const key = decryptAssymmetric({
ciphertext: latestFileKey.latestKey.encryptedKey, ciphertext: latestFileKey.latestKey.encryptedKey,
+98 -63
View File
@@ -4,10 +4,11 @@ import Link from "next/link";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import useTranslation from "next-translate/useTranslation"; import useTranslation from "next-translate/useTranslation";
import { import {
faBookOpen,
faGear, faGear,
faHouse, faKey,
faLink,
faMobile, faMobile,
faPlug,
faUser, faUser,
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
import { faPlus } 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 addUserToWorkspace from "~/pages/api/workspace/addUserToWorkspace";
import createWorkspace from "~/pages/api/workspace/createWorkspace"; import createWorkspace from "~/pages/api/workspace/createWorkspace";
import getWorkspaces from "~/pages/api/workspace/getWorkspaces"; import getWorkspaces from "~/pages/api/workspace/getWorkspaces";
import uploadKeys from "~/pages/api/workspace/uploadKeys";
import NavBarDashboard from "../navigation/NavBarDashboard"; import NavBarDashboard from "../navigation/NavBarDashboard";
import { import {
@@ -57,10 +59,10 @@ export default function Layout({ children }) {
const workspaces = await getWorkspaces(); const workspaces = await getWorkspaces();
const currentWorkspaces = workspaces.map((workspace) => workspace.name); const currentWorkspaces = workspaces.map((workspace) => workspace.name);
if (!currentWorkspaces.includes(workspaceName)) { if (!currentWorkspaces.includes(workspaceName)) {
const newWorkspace = await createWorkspace( const newWorkspace = await createWorkspace({
workspaceName, workspaceName,
localStorage.getItem("orgData.id") organizationId: localStorage.getItem("orgData.id"),
); });
let newWorkspaceId; let newWorkspaceId;
try { try {
newWorkspaceId = newWorkspace._id; newWorkspaceId = newWorkspace._id;
@@ -117,7 +119,7 @@ export default function Layout({ children }) {
href: href:
"/dashboard/" + workspaceMapping[workspaceSelected] + "?Development", "/dashboard/" + workspaceMapping[workspaceSelected] + "?Development",
title: t("nav:menu.secrets"), title: t("nav:menu.secrets"),
emoji: <FontAwesomeIcon icon={faHouse} />, emoji: <FontAwesomeIcon icon={faKey} />,
}, },
{ {
href: "/users/" + workspaceMapping[workspaceSelected], href: "/users/" + workspaceMapping[workspaceSelected],
@@ -127,7 +129,7 @@ export default function Layout({ children }) {
{ {
href: "/integrations/" + workspaceMapping[workspaceSelected], href: "/integrations/" + workspaceMapping[workspaceSelected],
title: t("nav:menu.integrations"), title: t("nav:menu.integrations"),
emoji: <FontAwesomeIcon icon={faLink} />, emoji: <FontAwesomeIcon icon={faPlug} />,
}, },
{ {
href: "/settings/project/" + workspaceMapping[workspaceSelected], href: "/settings/project/" + workspaceMapping[workspaceSelected],
@@ -160,7 +162,6 @@ export default function Layout({ children }) {
const intendedWorkspaceId = router.asPath const intendedWorkspaceId = router.asPath
.split("/") .split("/")
[router.asPath.split("/").length - 1].split("?")[0]; [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 a user is not a member of a workspace they are trying to access, just push them to one of theirs
if ( if (
intendedWorkspaceId != "heroku" && intendedWorkspaceId != "heroku" &&
@@ -217,66 +218,100 @@ export default function Layout({ children }) {
<NavBarDashboard /> <NavBarDashboard />
<div className="flex flex-col md:flex-row flex-1"> <div className="flex flex-col md:flex-row flex-1">
<aside className="bg-bunker-600 border-r border-mineshaft-500 w-full md:w-60 h-screen"> <aside className="bg-bunker-600 border-r border-mineshaft-500 w-full md:w-60 h-screen">
<nav> <nav className="flex flex-col justify-between items-between h-full">
<div className="py-6"></div> {/* <div className="py-6"></div> */}
<div className="flex justify-center w-full mt-7 mb-8 bg-bunker-600 w-full h-full flex flex-col items-center px-4"> <div>
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide"> <div className="flex justify-center w-full mt-[4.5rem] mb-6 bg-bunker-600 w-full h-20 flex flex-col items-center px-4">
{t("nav:menu.project")} <div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
{t("nav:menu.project")}
</div>
{workspaceList.length > 0 ? (
<Listbox
selected={workspaceSelected}
onChange={setWorkspaceSelected}
data={workspaceList}
buttonAction={openModal}
text=""
workspaceMapping={workspaceMapping}
/>
) : (
<Button
text="Add Project"
onButtonPressed={openModal}
color="mineshaft"
size="md"
icon={faPlus}
/>
)}
</div> </div>
{workspaceList.length > 0 ? ( <ul>
<Listbox {workspaceList.length > 0 &&
selected={workspaceSelected} menuItems.map(({ href, title, emoji }) => (
onChange={setWorkspaceSelected} <li className="mt-0.5 mx-2" key={title}>
data={workspaceList} {router.asPath.split("/")[1] === href.split("/")[1] &&
buttonAction={openModal} (["project", "billing", "org", "personal"].includes(
text="" router.asPath.split("/")[2]
workspaceMapping={workspaceMapping} )
/> ? router.asPath.split("/")[2] === href.split("/")[2]
) : ( : true) ? (
<Button
text="Add Project"
onButtonPressed={openModal}
color="mineshaft"
size="md"
icon={faPlus}
/>
)}
</div>
<ul>
{workspaceList.length > 0 &&
menuItems.map(({ href, title, emoji }) => (
<li className="mt-1.5 mx-2" key={title}>
{router.asPath.split("/")[1] === href.split("/")[1] &&
(["project", "billing", "org", "personal"].includes(
router.asPath.split("/")[2]
)
? router.asPath.split("/")[2] === href.split("/")[2]
: true) ? (
<div
className={`flex p-2 text-white text-sm rounded cursor-pointer bg-mineshaft-50/10`}
>
<div className="bg-primary w-1 rounded-xl mr-1"></div>
<p className="ml-2 mr-4">{emoji}</p>
{title}
</div>
) : router.asPath == "/noprojects" ? (
<div className={`flex p-2 text-white text-sm rounded`}>
<p className="ml-2 mr-4">{emoji}</p>
{title}
</div>
) : (
<Link href={href}>
<div <div
className={`flex p-2 text-white text-sm rounded cursor-pointer hover:bg-mineshaft-50/5`} className={`flex relative px-0.5 py-2.5 text-white text-sm rounded cursor-pointer bg-primary-50/10`}
> >
<p className="ml-2 mr-4">{emoji}</p> <div className="absolute top-0 my-1 ml-1 inset-0 bg-primary w-1 rounded-xl mr-1"></div>
<p className="w-6 ml-4 mr-2 flex items-center justify-center text-lg">
{emoji}
</p>
{title} {title}
</div> </div>
</Link> ) : router.asPath == "/noprojects" ? (
)} <div
</li> className={`flex p-2.5 text-white text-sm rounded`}
))} >
</ul> <p className="w-10 flex items-center justify-center text-lg">
{emoji}
</p>
{title}
</div>
) : (
<Link href={href}>
<div
className={`flex p-2.5 text-white text-sm rounded cursor-pointer hover:bg-primary-50/5`}
>
<p className="w-10 flex items-center justify-center text-lg">
{emoji}
</p>
{title}
</div>
</Link>
)}
</li>
))}
</ul>
</div>
<div className="w-full mt-40 mb-4 px-2">
{router.asPath.split("/")[1] === "home" ? (
<div
className={`flex relative px-0.5 py-2.5 text-white text-sm rounded cursor-pointer bg-primary-50/10`}
>
<div className="absolute top-0 my-1 ml-1 inset-0 bg-primary w-1 rounded-xl mr-1"></div>
<p className="w-6 ml-4 mr-2 flex items-center justify-center text-lg">
<FontAwesomeIcon icon={faBookOpen} />
</p>
Infisical Guide
</div>
) : (
<Link href={`/home/` + workspaceMapping[workspaceSelected]}>
<div
className={`flex p-2.5 text-white text-sm rounded cursor-pointer hover:bg-primary-50/5 mt-max border border-dashed border-bunker-400`}
>
<p className="w-10 flex items-center justify-center text-lg">
<FontAwesomeIcon icon={faBookOpen} />
</p>
Infisical Guide
</div>
</Link>
)}
</div>
</nav> </nav>
</aside> </aside>
<AddWorkspaceDialog <AddWorkspaceDialog
@@ -2,6 +2,19 @@ import React from "react";
import { faXmark } from "@fortawesome/free-solid-svg-icons"; import { faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
/**
* This is the notification that pops up at the bottom right when a user performs a certain action
* @param {object} org
* @param {string} org.buttonText - text of the button inside the notification
* @param {string} org.buttonLink - where the button leads to
* @param {string} org.titleText - the text at the top of a notification
* @param {string} org.emoji - the emoji in the notification title
* @param {string} org.textLine1 - first line of the text in the notification
* @param {string} org.textLine2 - second line of the text in the notification
* @param {string} org.setCheckDocsPopUpVisible - the functions that closes the popup
* @returns
*/
export default function BottonRightPopup({ export default function BottonRightPopup({
buttonText, buttonText,
buttonLink, buttonLink,
@@ -2,18 +2,10 @@ import React, { useEffect, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { faX } from "@fortawesome/free-solid-svg-icons"; import { faX } from "@fortawesome/free-solid-svg-icons";
import { reverseEnvMapping } from "../../../public/data/frequentConstants";
import guidGenerator from "../../utilities/randomId"; import guidGenerator from "../../utilities/randomId";
import Button from "../buttons/Button"; import Button from "../buttons/Button";
const roles = ["admin", "user"];
const reverseEnvMapping = {
dev: "Development",
staging: "Staging",
prod: "Production",
test: "Testing",
};
/** /**
* This is the component that we utilize for the user table - in future, can reuse it for some other purposes too. * This is the component that we utilize for the user table - in future, can reuse it for some other purposes too.
* #TODO: add the possibility of choosing and doing operations on multiple users. * #TODO: add the possibility of choosing and doing operations on multiple users.
+1 -1
View File
@@ -88,7 +88,7 @@ const UserTable = ({
}, [userData, myUser]); }, [userData, myUser]);
const grantAccess = async (id, publicKey) => { const grantAccess = async (id, publicKey) => {
let result = await getLatestFileKey(router.query.id); let result = await getLatestFileKey({workspaceId: router.query.id});
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY"); const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY");
+1 -1
View File
@@ -61,7 +61,7 @@ export default function Plan({ plan }) {
</button> </button>
</div> </div>
)} )}
<a href="/pricing" target='_blank rel="noopener"'> <a href="https://infisical.com/pricing" target='_blank rel="noopener"'>
<div className="relative z-10 text-gray-400 font-semibold hover:text-primary duration-200 cursor-pointer mb-0.5"> <div className="relative z-10 text-gray-400 font-semibold hover:text-primary duration-200 cursor-pointer mb-0.5">
{plan.buttonTextSecondary} {plan.buttonTextSecondary}
</div> </div>
@@ -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 (
<div className="flex-col w-full">
<div
className={`group relative flex flex-col justify-center w-full max-w-2xl border border-mineshaft-500 rounded-md`}
>
<input
onChange={(e) => 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"
/>
</div>
</div>
);
} else if (type === "value") {
return (
<div className="flex-col w-full">
<div
className={`group relative flex flex-col justify-center w-full max-w-2xl border border-mineshaft-500 rounded-md`}
>
<input
onChange={(e) => 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"
/>
<div
className={`${
blurred
? "text-bunker-800 group-hover:text-gray-400 peer-focus:text-gray-400 peer-active:text-gray-400"
: ""
} flex flex-row font-mono absolute z-0 ph-no-capture bg-bunker-800 h-9 rounded-md 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-100`}
>
{findReferences(value).map((texts, id) => {
if (id % 2 == 0 || texts.length <= 2) {
return (
<span className="ph-no-capture" key={id}>
{texts}
</span>
);
}
return (
<span className="ph-no-capture text-yellow" key={id}>
{texts.slice(0, 2)}
<span className="ph-no-capture text-yellow-200/80">
{texts.slice(2, texts.length - 1)}
</span>
{texts.slice(texts.length - 1, texts.length) == "}" ? (
<span className="ph-no-capture text-yellow">
{texts.slice(texts.length - 1, texts.length)}{" "}
</span>
) : (
<span className="ph-no-capture text-yellow-400">
{texts.slice(texts.length - 1, texts.length)}{" "}
</span>
)}
</span>
);
})}
</div>
{blurred && (
<div className="z-20 peer pr-2 bg-bunker-800 group-hover:hidden peer-hover:hidden peer-focus:hidden peer-active:invisible absolute h-9 w-fit max-w-xl rounded-md flex items-center text-gray-400/50 text-clip overflow-hidden">
<p className="ml-2"></p>
{value
.split("")
.slice(0, 42)
.map(() => (
<FontAwesomeIcon
key={guidGenerator()}
className="text-xxs mx-0.5"
icon={faCircle}
/>
))}
</div>
)}
</div>
</div>
);
}
};
export default React.memo(DashboardInputField);
@@ -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<HTMLDivElement | null>(null);
const syncScroll = (e: SyntheticEvent<HTMLDivElement>) => {
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 (
<div className="flex-col w-full">
<div
className={`group relative flex flex-col justify-center w-full max-w-2xl border ${
error ? "border-red" : "border-mineshaft-500"
} rounded-md`}
>
<input
onChange={(e) =>
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"
/>
</div>
{startsWithNumber && (
<p className="text-red text-xs mt-0.5 mx-1 mb-2 max-w-xs">
Should not start with a number
</p>
)}
{hasDuplicates && !startsWithNumber && (
<p className="text-red text-xs mt-0.5 mx-1 mb-2 max-w-xs">
Secret names should be unique
</p>
)}
</div>
);
} else if (type === "value") {
return (
<div className="flex-col w-full">
<div
className={`group relative whitespace-pre flex flex-col justify-center w-full max-w-2xl border border-mineshaft-500 rounded-md`}
>
<input
value={value}
onChange={(e) => 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"
/>
<div
ref={ref}
className={`${
blurred
? "text-bunker-800 group-hover:text-gray-400 peer-focus:text-gray-400 peer-active:text-gray-400"
: ""
} absolute flex flex-row whitespace-pre font-mono z-0 ph-no-capture max-w-2xl overflow-x-scroll bg-bunker-800 h-9 rounded-md text-gray-400 text-md px-2 py-1.5 w-full min-w-16 outline-none focus:ring-2 focus:ring-primary/50 duration-100 no-scrollbar no-scrollbar::-webkit-scrollbar`}
>
{value.split(REGEX).map((word, id) => {
if (word.match(REGEX) !== null) {
return (
<span className="ph-no-capture text-yellow" key={id}>
{word.slice(0, 2)}
<span className="ph-no-capture text-yellow-200/80">
{word.slice(2, word.length - 1)}
</span>
{word.slice(word.length - 1, word.length) == "}" ? (
<span className="ph-no-capture text-yellow">
{word.slice(word.length - 1, word.length)}
</span>
) : (
<span className="ph-no-capture text-yellow-400">
{word.slice(word.length - 1, word.length)}
</span>
)}
</span>
);
} else {
return (
<span key={id} className="ph-no-capture">
{word}
</span>
);
}
})}
</div>
{blurred && (
<div className="absolute flex flex-row items-center z-20 peer pr-2 bg-bunker-800 group-hover:hidden peer-hover:hidden peer-focus:hidden peer-active:invisible h-9 w-full max-w-2xl rounded-md text-gray-400/50 text-clip">
<div className="px-2 flex flex-row items-center overflow-x-scroll no-scrollbar no-scrollbar::-webkit-scrollbar">
{value.split("").map(() => (
<FontAwesomeIcon
key={guidGenerator()}
className="text-xxs mx-0.5"
icon={faCircle}
/>
))}
</div>
</div>
)}
</div>
</div>
);
}
return <>Something Wrong</>;
};
export default React.memo(DashboardInputField);
@@ -1,4 +1,4 @@
import { useState } from "react"; import { type ChangeEvent, type DragEvent, useState } from "react";
import Image from "next/image"; import Image from "next/image";
import useTranslation from "next-translate/useTranslation"; import useTranslation from "next-translate/useTranslation";
import { faUpload } from "@fortawesome/free-solid-svg-icons"; import { faUpload } from "@fortawesome/free-solid-svg-icons";
@@ -9,29 +9,39 @@ import Error from "../basic/Error";
import parse from "../utilities/file"; import parse from "../utilities/file";
import guidGenerator from "../utilities/randomId"; 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 = ({ const DropZone = ({
setData, setData,
setErrorDragAndDrop, setErrorDragAndDrop,
createNewFile, createNewFile,
errorDragAndDrop, errorDragAndDrop,
addPresetRow,
setButtonReady, setButtonReady,
keysExist, keysExist,
numCurrentRows, numCurrentRows,
}) => { }: DropZoneProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const handleDragEnter = (e) => { const handleDragEnter = (e: DragEvent) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
}; };
const handleDragLeave = (e) => { const handleDragLeave = (e: DragEvent) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
}; };
const handleDragOver = (e) => { const handleDragOver = (e: DragEvent) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
@@ -42,22 +52,25 @@ const DropZone = ({
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
// This function function immediately parses the file after it is dropped // This function function immediately parses the file after it is dropped
const handleDrop = async (e) => { const handleDrop = async (e: DragEvent) => {
setLoading(true); setLoading(true);
setTimeout(() => setLoading(false), 5000); setTimeout(() => setLoading(false), 5000);
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
e.dataTransfer.dropEffect = "copy"; e.dataTransfer.dropEffect = "copy";
var file = e.dataTransfer.files[0], const file = e.dataTransfer.files[0];
reader = new FileReader(); const reader = new FileReader();
reader.onload = function (event) {
const keyPairs = parse(event.target.result); 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) => [ const newData = Object.keys(keyPairs).map((key, index) => [
guidGenerator(), guidGenerator(),
numCurrentRows + index, numCurrentRows + index,
key, key,
keyPairs[key], keyPairs[key as keyof typeof keyPairs],
"shared", "shared",
]); ]);
setData(newData); 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) // 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<HTMLInputElement>) => {
setLoading(true); setLoading(true);
setTimeout(() => setLoading(false), 5000); setTimeout(() => setLoading(false), 5000);
var file = e.target.files[0], if (e.currentTarget.files === null) return;
reader = new FileReader(); const file = e.currentTarget.files[0];
reader.onload = function (event) { const reader = new FileReader();
const newData = event.target.result reader.onload = (event) => {
.split("\n") if (event.target === null || event.target.result === null) return;
.map((line, index) => [ const { result } = event.target;
guidGenerator(), if (typeof result === "string") {
numCurrentRows + index, const newData = result
line.split("=")[0], .split("\n")
line.split("=").slice(1, line.split("=").length).join("="), .map((line: string, index: number) => [
"shared", guidGenerator(),
]); numCurrentRows + index,
setData(newData); line.split("=")[0],
setButtonReady(true); line.split("=").slice(1, line.split("=").length).join("="),
"shared",
]);
setData(newData);
setButtonReady(true);
}
}; };
reader.readAsText(file); reader.readAsText(file);
}; };
@@ -108,17 +126,17 @@ const DropZone = ({
) : keysExist ? ( ) : keysExist ? (
<div <div
className="opacity-60 hover:opacity-100 duration-200 relative bg-bunker outline max-w-[calc(100%-1rem)] w-full outline-dashed outline-gray-600 rounded-md outline-2 flex flex-col items-center justify-center mb-16 mx-auto mt-1 py-8 px-2" className="opacity-60 hover:opacity-100 duration-200 relative bg-bunker outline max-w-[calc(100%-1rem)] w-full outline-dashed outline-gray-600 rounded-md outline-2 flex flex-col items-center justify-center mb-16 mx-auto mt-1 py-8 px-2"
onDragEnter={(e) => handleDragEnter(e)} onDragEnter={handleDragEnter}
onDragOver={(e) => handleDragOver(e)} onDragOver={handleDragOver}
onDragLeave={(e) => handleDragLeave(e)} onDragLeave={handleDragLeave}
onDrop={(e) => handleDrop(e)} onDrop={handleDrop}
> >
<input <input
id="fileSelect" id="fileSelect"
type="file" type="file"
className="opacity-0 absolute w-full h-full" className="opacity-0 absolute w-full h-full"
accept=".txt,.env" accept=".txt,.env"
onChange={(e) => handleFileSelect(e)} onChange={handleFileSelect}
/> />
{errorDragAndDrop ? ( {errorDragAndDrop ? (
<div className="my-3 max-w-xl opacity-80"></div> <div className="my-3 max-w-xl opacity-80"></div>
@@ -143,10 +161,10 @@ const DropZone = ({
) : ( ) : (
<div <div
className="opacity-80 hover:opacity-100 duration-200 relative bg-bunker outline max-w-2xl w-full outline-dashed outline-gray-700 rounded-md outline-2 flex flex-col items-center justify-center pt-16 mb-16 px-4" className="opacity-80 hover:opacity-100 duration-200 relative bg-bunker outline max-w-2xl w-full outline-dashed outline-gray-700 rounded-md outline-2 flex flex-col items-center justify-center pt-16 mb-16 px-4"
onDragEnter={(e) => handleDragEnter(e)} onDragEnter={handleDragEnter}
onDragOver={(e) => handleDragOver(e)} onDragOver={handleDragOver}
onDragLeave={(e) => handleDragLeave(e)} onDragLeave={handleDragLeave}
onDrop={(e) => handleDrop(e)} onDrop={handleDrop}
> >
<FontAwesomeIcon icon={faUpload} className="text-7xl mb-8" /> <FontAwesomeIcon icon={faUpload} className="text-7xl mb-8" />
<p className="">{t("common:drop-zone")}</p> <p className="">{t("common:drop-zone")}</p>
@@ -155,7 +173,7 @@ const DropZone = ({
type="file" type="file"
className="opacity-0 absolute w-full h-full" className="opacity-0 absolute w-full h-full"
accept=".txt,.env" accept=".txt,.env"
onChange={(e) => handleFileSelect(e)} onChange={handleFileSelect}
/> />
<div className="flex flex-row w-full items-center justify-center mb-6 mt-5"> <div className="flex flex-row w-full items-center justify-center mb-6 mt-5">
<div className="border-t border-gray-700 w-1/5"></div> <div className="border-t border-gray-700 w-1/5"></div>
@@ -21,6 +21,7 @@ import getOrganization from "../../pages/api/organization/GetOrg";
import getOrganizations from "../../pages/api/organization/getOrgs"; import getOrganizations from "../../pages/api/organization/getOrgs";
import getUser from "../../pages/api/user/getUser"; import getUser from "../../pages/api/user/getUser";
import guidGenerator from "../utilities/randomId"; import guidGenerator from "../utilities/randomId";
import logout from "~/pages/api/auth/Logout";
/** /**
* @param {(key: string) => string} t * @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 router = useRouter();
const [user, setUser] = useState({}); const [user, setUser] = useState<IUser | undefined>();
const [orgs, setOrgs] = useState([]); const [orgs, setOrgs] = useState([]);
const [currentOrg, setCurrentOrg] = useState([]); const [currentOrg, setCurrentOrg] = useState<ICurrentOrg | undefined>();
const { t } = useTranslation(""); const { t } = useTranslation("");
const supportOptionsList = useMemo(() => supportOptions(t), [t]); const supportOptionsList = useMemo(() => supportOptions(t), [t]);
useEffect(async () => { useEffect(() => {
const userData = await getUser(); (async () => {
setUser(userData); const userData = await getUser();
const orgsData = await getOrganizations(); setUser(userData);
setOrgs(orgsData); const orgsData = await getOrganizations();
const currentOrg = await getOrganization({ setOrgs(orgsData);
orgId: localStorage.getItem("orgData.id"), const currentOrg = await getOrganization({
}); orgId: String(localStorage.getItem("orgData.id")),
setCurrentOrg(currentOrg); });
setCurrentOrg(currentOrg);
})();
}, []); }, []);
const closeApp = async () => { const closeApp = async () => {
console.log("Logging out..."); console.log("Logging out...");
await logout(); await logout();
router.push("/"); router.push("/login");
}; };
return ( return (
@@ -110,8 +128,8 @@ export default function Navbar({ onButtonPressed }) {
<a <a
key={guidGenerator()} key={guidGenerator()}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener"
href={url} href={String(url)}
className="font-normal text-gray-300 duration-200 rounded-md w-full flex items-center py-0.5" className="font-normal text-gray-300 duration-200 rounded-md w-full flex items-center py-0.5"
> >
<div className="relative flex justify-start items-center cursor-pointer select-none py-2 px-2 rounded-md text-gray-400 hover:bg-white/10 duration-200 hover:text-gray-200 w-full"> <div className="relative flex justify-start items-center cursor-pointer select-none py-2 px-2 rounded-md text-gray-400 hover:bg-white/10 duration-200 hover:text-gray-200 w-full">
@@ -241,9 +259,10 @@ export default function Navbar({ onButtonPressed }) {
<div className="flex flex-col items-start px-1 mt-3 mb-2"> <div className="flex flex-col items-start px-1 mt-3 mb-2">
{orgs {orgs
.filter( .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 }) => (
<div <div
key={guidGenerator()} key={guidGenerator()}
onClick={() => { onClick={() => {
@@ -1,29 +1,37 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { faCcMastercard, faCcVisa } from "@fortawesome/free-brands-svg-icons";
import { import {
faAngleRight, faAngleRight,
faQuestionCircle,
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
import { faCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import getOrganization from "~/pages/api/organization/GetOrg"; 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 [orgName, setOrgName] = useState("");
const [workspaceName, setWorkspaceName] = useState(""); const [workspaceName, setWorkspaceName] = useState("");
const router = useRouter(); const router = useRouter();
useEffect(() => { useEffect(() => {
(async () => { (async () => {
let org = await getOrganization({ const orgId = localStorage.getItem("orgData.id")
orgId: localStorage.getItem("orgData.id"), const org = await getOrganization({
orgId: orgId ? orgId : "",
}); });
setOrgName(org.name); setOrgName(org.name);
let workspace = await getWorkspaceInfo({
workspaceId: router.query.id, const workspace = await getProjectInfo({
projectId: String(router.query.id),
}); });
setWorkspaceName(workspace.name); setWorkspaceName(workspace.name);
})(); })();
@@ -1,8 +1,6 @@
import { PATH } from "~/const";
import token from "~/pages/api/auth/Token"; import token from "~/pages/api/auth/Token";
export default class SecurityClient { export default class SecurityClient {
static authOrigins = [PATH];
static #token = ""; static #token = "";
contructor() {} contructor() {}
@@ -13,13 +11,12 @@ export default class SecurityClient {
static async fetchCall(resource, options) { static async fetchCall(resource, options) {
let req = new Request(resource, options); let req = new Request(resource, options);
const destOrigin = new URL(req.url).origin;
if (this.#token == "") { if (this.#token == "") {
this.setToken(await token()); this.setToken(await token());
} }
if (this.#token && this.authOrigins.includes(destOrigin)) { if (this.#token) {
req.headers.set("Authorization", "Bearer " + this.#token); req.headers.set("Authorization", "Bearer " + this.#token);
return fetch(req); return fetch(req);
} }
+15 -7
View File
@@ -43,7 +43,7 @@ const attemptLogin = async (
let serverPublicKey, salt; let serverPublicKey, salt;
try { try {
const res = await login1(email, clientPublicKey); let res = await login1(email, clientPublicKey);
res = await res.json(); res = await res.json();
serverPublicKey = res.serverPublicKey; serverPublicKey = res.serverPublicKey;
salt = res.salt; salt = res.salt;
@@ -134,10 +134,18 @@ const attemptLogin = async (
// If user is logging in for the first time, add the example keys // If user is logging in for the first time, add the example keys
if (isSignUp) { if (isSignUp) {
await pushKeys( await pushKeys({
{ obj: {
DATABASE_URL: [ 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", "personal",
], ],
TWILIO_AUTH_TOKEN: [ TWILIO_AUTH_TOKEN: [
@@ -147,9 +155,9 @@ const attemptLogin = async (
WEBSITE_URL: ["http://localhost:3000", "shared"], WEBSITE_URL: ["http://localhost:3000", "shared"],
STRIPE_SECRET_KEY: ["sk_test_7348oyho4hfq398HIUOH78", "shared"], STRIPE_SECRET_KEY: ["sk_test_7348oyho4hfq398HIUOH78", "shared"],
}, },
projectToLogin, workspaceId: projectToLogin,
"Development" env: "Development"
); });
} }
try { try {
if (email) { if (email) {
@@ -108,11 +108,7 @@ const changePassword = async (
} }
); );
} catch (error) { } catch (error) {
console.log( console.log("Something went wrong during changing the password");
"Something went wrong during changing the password",
slat,
serverPublicKey
);
} }
return true; return true;
}; };
@@ -2,6 +2,12 @@ const nacl = require("tweetnacl");
nacl.util = require("tweetnacl-util"); nacl.util = require("tweetnacl-util");
const aes = require("./aes-256-gcm"); const aes = require("./aes-256-gcm");
type encryptAsymmetricProps = {
plaintext: string;
publicKey: string;
privateKey: string;
}
/** /**
* Return assymmetrically encrypted [plaintext] using [publicKey] where * Return assymmetrically encrypted [plaintext] using [publicKey] where
* [publicKey] likely belongs to the recipient. * [publicKey] likely belongs to the recipient.
@@ -13,7 +19,7 @@ const aes = require("./aes-256-gcm");
* @returns {String} ciphertext - base64-encoded ciphertext * @returns {String} ciphertext - base64-encoded ciphertext
* @returns {String} nonce - base64-encoded nonce * @returns {String} nonce - base64-encoded nonce
*/ */
const encryptAssymmetric = ({ plaintext, publicKey, privateKey }) => { const encryptAssymmetric = ({ plaintext, publicKey, privateKey }: encryptAsymmetricProps): object => {
const nonce = nacl.randomBytes(24); const nonce = nacl.randomBytes(24);
const ciphertext = nacl.box( const ciphertext = nacl.box(
nacl.util.decodeUTF8(plaintext), 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 * Return assymmetrically decrypted [ciphertext] using [privateKey] where
* [privateKey] likely belongs to the recipient. * [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} obj.privateKey - base64-encoded private key of the receiver (current user)
* @param {String} plaintext - UTF8 plaintext * @param {String} plaintext - UTF8 plaintext
*/ */
const decryptAssymmetric = ({ ciphertext, nonce, publicKey, privateKey }) => { const decryptAssymmetric = ({ ciphertext, nonce, publicKey, privateKey }: decryptAsymmetricProps): string => {
const plaintext = nacl.box.open( const plaintext = nacl.box.open(
nacl.util.decodeBase64(ciphertext), nacl.util.decodeBase64(ciphertext),
nacl.util.decodeBase64(nonce), nacl.util.decodeBase64(nonce),
@@ -49,13 +62,18 @@ const decryptAssymmetric = ({ ciphertext, nonce, publicKey, privateKey }) => {
return nacl.util.encodeUTF8(plaintext); return nacl.util.encodeUTF8(plaintext);
}; };
type encryptSymmetricProps = {
plaintext: string;
key: string;
}
/** /**
* Return symmetrically encrypted [plaintext] using [key]. * Return symmetrically encrypted [plaintext] using [key].
* @param {Object} obj * @param {Object} obj
* @param {String} obj.plaintext - plaintext to encrypt * @param {String} obj.plaintext - plaintext to encrypt
* @param {String} obj.key - 16-byte hex key * @param {String} obj.key - 16-byte hex key
*/ */
const encryptSymmetric = ({ plaintext, key }) => { const encryptSymmetric = ({ plaintext, key }: encryptSymmetricProps): object => {
let ciphertext, iv, tag; let ciphertext, iv, tag;
try { try {
const obj = aes.encrypt(plaintext, key); 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], * Return symmetrically decypted [ciphertext] using [iv], [tag],
* and [key]. * and [key].
@@ -85,7 +110,7 @@ const encryptSymmetric = ({ plaintext, key }) => {
* @param {String} obj.key - 32-byte hex 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; let plaintext;
try { try {
plaintext = aes.decrypt(ciphertext, iv, tag, key); plaintext = aes.decrypt(ciphertext, iv, tag, key);
@@ -97,9 +122,9 @@ const decryptSymmetric = ({ ciphertext, iv, tag, key }) => {
return plaintext; return plaintext;
}; };
module.exports = { export {
encryptAssymmetric,
decryptAssymmetric, decryptAssymmetric,
encryptSymmetric,
decryptSymmetric, decryptSymmetric,
encryptAssymmetric,
encryptSymmetric,
}; };
@@ -1,5 +1,6 @@
import getSecrets from "~/pages/api/files/GetSecrets"; import getSecrets from "~/pages/api/files/GetSecrets";
import { envMapping } from "../../../public/data/frequentConstants";
import guidGenerator from "../randomId"; import guidGenerator from "../randomId";
const { const {
@@ -9,13 +10,6 @@ const {
const nacl = require("tweetnacl"); const nacl = require("tweetnacl");
nacl.util = require("tweetnacl-util"); nacl.util = require("tweetnacl-util");
const envMapping = {
Development: "dev",
Staging: "staging",
Production: "prod",
Testing: "test",
};
const getSecretsForProject = async ({ const getSecretsForProject = async ({
env, env,
setFileState, setFileState,
@@ -2,29 +2,35 @@ import uploadSecrets from "~/pages/api/files/UploadSecrets";
import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey"; import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey";
import getWorkspaceKeys from "~/pages/api/workspace/getWorkspaceKeys"; import getWorkspaceKeys from "~/pages/api/workspace/getWorkspaceKeys";
import { envMapping } from "../../../public/data/frequentConstants";
const crypto = require("crypto"); const crypto = require("crypto");
const { const {
decryptAssymmetric, decryptAssymmetric,
decryptSymmetric,
encryptSymmetric, encryptSymmetric,
encryptAssymmetric, encryptAssymmetric,
} = require("../cryptography/crypto"); } = require("../cryptography/crypto");
const nacl = require("tweetnacl"); const nacl = require("tweetnacl");
nacl.util = require("tweetnacl-util"); nacl.util = require("tweetnacl-util");
const envMapping = { export interface IK {
Development: "dev", publicKey: string;
Staging: "staging", userId: string;
Production: "prod", }
Testing: "test",
};
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"); const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY");
let randomBytes; let randomBytes: string;
if (Object.keys(sharedKey).length > 0) { if (Object.keys(sharedKey).length > 0) {
// case: a (shared) key exists for the workspace // case: a (shared) key exists for the workspace
randomBytes = decryptAssymmetric({ randomBytes = decryptAssymmetric({
@@ -55,11 +61,11 @@ const pushKeys = async (obj, workspaceId, env) => {
iv: ivValue, iv: ivValue,
tag: tagValue, tag: tagValue,
} = encryptSymmetric({ } = encryptSymmetric({
plaintext: obj[key][0], plaintext: obj[key as keyof typeof obj][0],
key: randomBytes, 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 { return {
ciphertextKey, ciphertextKey,
@@ -69,7 +75,7 @@ const pushKeys = async (obj, workspaceId, env) => {
ciphertextValue, ciphertextValue,
ivValue, ivValue,
tagValue, 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, type: visibility,
}; };
}); });
@@ -80,7 +86,7 @@ const pushKeys = async (obj, workspaceId, env) => {
}); });
// assymmetrically encrypt key with each receiver public keys // assymmetrically encrypt key with each receiver public keys
const keys = publicKeys.map((k) => { const keys = publicKeys.map((k: IK) => {
const { ciphertext, nonce } = encryptAssymmetric({ const { ciphertext, nonce } = encryptAssymmetric({
plaintext: randomBytes, plaintext: randomBytes,
publicKey: k.publicKey, publicKey: k.publicKey,
@@ -99,7 +105,7 @@ const pushKeys = async (obj, workspaceId, env) => {
workspaceId, workspaceId,
secrets, secrets,
keys, keys,
environment: envMapping[env], environment: envMapping[env as keyof typeof envMapping],
}); });
}; };
-2
View File
@@ -1,5 +1,3 @@
export const PATH = process.env.NEXT_PUBLIC_WEBSITE_URL;
export const publicPaths = [ export const publicPaths = [
`/`, `/`,
// `/integrations`, // `/integrations`,
+1 -1
View File
@@ -209,7 +209,7 @@
"description": "Manage your integrations of Infisical with third-party services.", "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-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.", "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-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 [email protected]." "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 [email protected]."
} }
+4 -8
View File
@@ -2,16 +2,12 @@
const nextTranslate = require("next-translate"); const nextTranslate = require("next-translate");
const ContentSecurityPolicy = ` const ContentSecurityPolicy = `
default-src ${process.env.NEXT_PUBLIC_WEBSITE_URL}; default-src 'self';
script-src ${ script-src 'self' https://app.posthog.com https://js.stripe.com https://api.stripe.com 'unsafe-inline' 'unsafe-eval';
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';
style-src 'self' https://rsms.me 'unsafe-inline'; 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; frame-src https://js.stripe.com/ https://api.stripe.com;
connect-src ws://${process.env.NEXT_PUBLIC_WEBSITE_URL?.split("//")[1]} ${ 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;
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;
img-src 'self' https://*.stripe.com https://i.ytimg.com/ data:; img-src 'self' https://*.stripe.com https://i.ytimg.com/ data:;
media-src; media-src;
font-src 'self' https://maxcdn.bootstrapcdn.com https://rsms.me https://fonts.gstatic.com; font-src 'self' https://maxcdn.bootstrapcdn.com https://rsms.me https://fonts.gstatic.com;
+378 -409
View File
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -54,9 +54,12 @@
"devDependencies": { "devDependencies": {
"@tailwindcss/typography": "^0.5.4", "@tailwindcss/typography": "^0.5.4",
"@types/node": "18.11.9", "@types/node": "18.11.9",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
"autoprefixer": "^10.4.7", "autoprefixer": "^10.4.7",
"eslint": "^8.28.0", "eslint": "^8.29.0",
"eslint-config-next": "^13.0.5", "eslint-config-next": "^13.0.5",
"eslint-import-resolver-typescript": "^3.5.2",
"eslint-plugin-simple-import-sort": "^8.0.0", "eslint-plugin-simple-import-sort": "^8.0.0",
"postcss": "^8.4.14", "postcss": "^8.4.14",
"prettier": "2.7.1", "prettier": "2.7.1",
+1 -2
View File
@@ -1,4 +1,3 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
/** /**
@@ -14,7 +13,7 @@ const changePassword2 = ({
verifier, verifier,
clientProof, clientProof,
}) => { }) => {
return SecurityClient.fetchCall(PATH + "/api/v1/password/change-password", { return SecurityClient.fetchCall("/api/v1/password/change-password", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
+1 -2
View File
@@ -1,4 +1,3 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient.js"; import SecurityClient from "~/utilities/SecurityClient.js";
/** /**
@@ -9,7 +8,7 @@ import SecurityClient from "~/utilities/SecurityClient.js";
* @returns * @returns
*/ */
const checkAuth = async (req, res) => { const checkAuth = async (req, res) => {
return SecurityClient.fetchCall(PATH + "/api/v1/auth/checkAuth", { return SecurityClient.fetchCall("/api/v1/auth/checkAuth", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -1,5 +1,3 @@
import { PATH } from "~/const";
/** /**
* This route check the verification code from the email that user just recieved * This route check the verification code from the email that user just recieved
* @param {*} email * @param {*} email
@@ -7,7 +5,7 @@ import { PATH } from "~/const";
* @returns * @returns
*/ */
const checkEmailVerificationCode = (email, code) => { const checkEmailVerificationCode = (email, code) => {
return fetch(PATH + "/api/v1/signup/email/verify", { return fetch("/api/v1/signup/email/verify", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -1,5 +1,3 @@
import { PATH } from "~/const";
/** /**
* This function is called in the end of the signup process. * This function is called in the end of the signup process.
* It sends all the necessary nformation to the server. * It sends all the necessary nformation to the server.
@@ -28,7 +26,7 @@ const completeAccountInformationSignup = ({
verifier, verifier,
token, token,
}) => { }) => {
return fetch(PATH + "/api/v1/signup/complete-account/signup", { return fetch("/api/v1/signup/complete-account/signup", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -1,5 +1,3 @@
import { PATH } from "~/const";
/** /**
* This function is called in the end of the signup process. * This function is called in the end of the signup process.
* It sends all the necessary nformation to the server. * It sends all the necessary nformation to the server.
@@ -26,7 +24,7 @@ const completeAccountInformationSignupInvite = ({
verifier, verifier,
token, token,
}) => { }) => {
return fetch(PATH + "/api/v1/signup/complete-account/invite", { return fetch("/api/v1/signup/complete-account/invite", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -1,4 +1,3 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
/** /**
@@ -13,7 +12,7 @@ const issueBackupPrivateKey = ({
clientProof, clientProof,
}) => { }) => {
return SecurityClient.fetchCall( return SecurityClient.fetchCall(
PATH + "/api/v1/password/backup-private-key", "/api/v1/password/backup-private-key",
{ {
method: "POST", method: "POST",
headers: { headers: {
@@ -32,8 +31,8 @@ const issueBackupPrivateKey = ({
if (res.status == 200) { if (res.status == 200) {
return res; return res;
} else { } else {
return res;
console.log("Failed to issue the backup key"); console.log("Failed to issue the backup key");
return res;
} }
}); });
}; };
+1 -3
View File
@@ -1,5 +1,3 @@
import { PATH } from "~/const";
/** /**
* This is the first step of the login process (pake) * This is the first step of the login process (pake)
* @param {*} email * @param {*} email
@@ -7,7 +5,7 @@ import { PATH } from "~/const";
* @returns * @returns
*/ */
const login1 = (email, clientPublicKey) => { const login1 = (email, clientPublicKey) => {
return fetch(PATH + "/api/v1/auth/login1", { return fetch("/api/v1/auth/login1", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
+1 -3
View File
@@ -1,5 +1,3 @@
import { PATH } from "~/const";
/** /**
* This is the second step of the login process * This is the second step of the login process
* @param {*} email * @param {*} email
@@ -7,7 +5,7 @@ import { PATH } from "~/const";
* @returns * @returns
*/ */
const login2 = (email, clientProof) => { const login2 = (email, clientProof) => {
return fetch(PATH + "/api/v1/auth/login2", { return fetch("/api/v1/auth/login2", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -1,22 +1,18 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
/** /**
* This route logs the user out. Note: the user should authorized to do this. * 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 * 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) => { const logout = async () => {
return SecurityClient.fetchCall(PATH + "/api/v1/auth/logout", { return SecurityClient.fetchCall("/api/v1/auth/logout", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
credentials: "include", credentials: "include",
}).then((res) => { }).then((res) => {
if (res.status == 200) { if (res?.status == 200) {
SecurityClient.setToken(""); SecurityClient.setToken("");
// Delete the cookie by not setting a value; Alternatively clear the local storage // Delete the cookie by not setting a value; Alternatively clear the local storage
localStorage.setItem("publicKey", ""); localStorage.setItem("publicKey", "");
+1 -2
View File
@@ -1,4 +1,3 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
/** /**
@@ -7,7 +6,7 @@ import SecurityClient from "~/utilities/SecurityClient";
* @returns * @returns
*/ */
const SRP1 = ({ clientPublicKey }) => { const SRP1 = ({ clientPublicKey }) => {
return SecurityClient.fetchCall(PATH + "/api/v1/password/srp1", { return SecurityClient.fetchCall("/api/v1/password/srp1", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -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) * This route send the verification email to the user's email (contains a 6-digit verification code)
* @param {*} email * @param {*} email
*/ */
const sendVerificationEmail = (email) => { const sendVerificationEmail = (email) => {
fetch(PATH + "/api/v1/signup/email/signup", { fetch("/api/v1/signup/email/signup", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
+1 -3
View File
@@ -1,7 +1,5 @@
import { PATH } from "~/const";
const token = async (req, res) => { const token = async (req, res) => {
return fetch(PATH + "/api/v1/auth/token", { return fetch("/api/v1/auth/token", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -1,5 +1,3 @@
import { PATH } from "~/const";
/** /**
* This route verifies the signup invite link * This route verifies the signup invite link
* @param {*} email * @param {*} email
@@ -7,7 +5,7 @@ import { PATH } from "~/const";
* @returns * @returns
*/ */
const verifySignupInvite = ({ email, code }) => { const verifySignupInvite = ({ email, code }) => {
return fetch(PATH + "/api/v1/invite-org/verify", { return fetch("/api/v1/invite-org/verify", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -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. * 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 * @param {*} req
@@ -7,7 +5,7 @@ import { PATH } from "~/const";
* @returns * @returns
*/ */
const publicKeyInfisical = (req, res) => { const publicKeyInfisical = (req, res) => {
return fetch(PATH + "/api/v1/key/publicKey/infisical", { return fetch("/api/v1/key/publicKey/infisical", {
method: "GET", method: "GET",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
-2
View File
@@ -1,4 +1,3 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient.js"; import SecurityClient from "~/utilities/SecurityClient.js";
/** /**
@@ -9,7 +8,6 @@ import SecurityClient from "~/utilities/SecurityClient.js";
*/ */
const getSecrets = async (workspaceId, env) => { const getSecrets = async (workspaceId, env) => {
return SecurityClient.fetchCall( return SecurityClient.fetchCall(
PATH +
"/api/v1/secret/" + "/api/v1/secret/" +
workspaceId + workspaceId +
"?" + "?" +
+1 -2
View File
@@ -1,4 +1,3 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
/** /**
@@ -8,7 +7,7 @@ import SecurityClient from "~/utilities/SecurityClient";
* @returns * @returns
*/ */
const uploadSecrets = async ({ workspaceId, secrets, keys, environment }) => { const uploadSecrets = async ({ workspaceId, secrets, keys, environment }) => {
return SecurityClient.fetchCall(PATH + "/api/v1/secret/" + workspaceId, { return SecurityClient.fetchCall("/api/v1/secret/" + workspaceId, {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -1,9 +1,8 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
const changeHerokuConfigVars = ({ integrationId, key, secrets }) => { const changeHerokuConfigVars = ({ integrationId, key, secrets }) => {
return SecurityClient.fetchCall( return SecurityClient.fetchCall(
PATH + "/api/v1/integration/" + integrationId + "/sync", "/api/v1/integration/" + integrationId + "/sync",
{ {
method: "POST", method: "POST",
headers: { headers: {
@@ -1,4 +1,3 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
/** /**
@@ -8,7 +7,7 @@ import SecurityClient from "~/utilities/SecurityClient";
*/ */
const deleteIntegration = ({ integrationId }) => { const deleteIntegration = ({ integrationId }) => {
return SecurityClient.fetchCall( return SecurityClient.fetchCall(
PATH + "/api/v1/integration/" + integrationId, "/api/v1/integration/" + integrationId,
{ {
method: "DELETE", method: "DELETE",
headers: { headers: {
@@ -1,4 +1,3 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
/** /**
@@ -8,7 +7,7 @@ import SecurityClient from "~/utilities/SecurityClient";
*/ */
const deleteIntegrationAuth = ({ integrationAuthId }) => { const deleteIntegrationAuth = ({ integrationAuthId }) => {
return SecurityClient.fetchCall( return SecurityClient.fetchCall(
PATH + "/api/v1/integration-auth/" + integrationAuthId, "/api/v1/integration-auth/" + integrationAuthId,
{ {
method: "DELETE", method: "DELETE",
headers: { headers: {
@@ -1,9 +1,8 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
const getIntegrationApps = ({ integrationAuthId }) => { const getIntegrationApps = ({ integrationAuthId }) => {
return SecurityClient.fetchCall( return SecurityClient.fetchCall(
PATH + "/api/v1/integration-auth/" + integrationAuthId + "/apps", "/api/v1/integration-auth/" + integrationAuthId + "/apps",
{ {
method: "GET", method: "GET",
headers: { headers: {
@@ -1,8 +1,7 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
const getIntegrations = () => { const getIntegrations = () => {
return SecurityClient.fetchCall(PATH + "/api/v1/integration/integrations", { return SecurityClient.fetchCall("/api/v1/integration/integrations", {
method: "GET", method: "GET",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -1,4 +1,3 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
/** /**
@@ -8,7 +7,7 @@ import SecurityClient from "~/utilities/SecurityClient";
*/ */
const startIntegration = ({ integrationId, appName, environment }) => { const startIntegration = ({ integrationId, appName, environment }) => {
return SecurityClient.fetchCall( return SecurityClient.fetchCall(
PATH + "/api/v1/integration/" + integrationId, "/api/v1/integration/" + integrationId,
{ {
method: "PATCH", method: "PATCH",
headers: { headers: {
@@ -1,4 +1,3 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
/** /**
@@ -8,7 +7,7 @@ import SecurityClient from "~/utilities/SecurityClient";
*/ */
const AuthorizeIntegration = ({ workspaceId, code, integration }) => { const AuthorizeIntegration = ({ workspaceId, code, integration }) => {
return SecurityClient.fetchCall( return SecurityClient.fetchCall(
PATH + "/api/v1/integration-auth/oauth-token", "/api/v1/integration-auth/oauth-token",
{ {
method: "POST", method: "POST",
headers: { headers: {
@@ -1,4 +1,3 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
/** /**
@@ -8,7 +7,7 @@ import SecurityClient from "~/utilities/SecurityClient";
*/ */
const getWorkspaceAuthorizations = ({ workspaceId }) => { const getWorkspaceAuthorizations = ({ workspaceId }) => {
return SecurityClient.fetchCall( return SecurityClient.fetchCall(
PATH + "/api/v1/workspace/" + workspaceId + "/authorizations", "/api/v1/workspace/" + workspaceId + "/authorizations",
{ {
method: "GET", method: "GET",
headers: { headers: {
@@ -1,4 +1,3 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
/** /**
@@ -8,7 +7,7 @@ import SecurityClient from "~/utilities/SecurityClient";
*/ */
const getWorkspaceIntegrations = ({ workspaceId }) => { const getWorkspaceIntegrations = ({ workspaceId }) => {
return SecurityClient.fetchCall( return SecurityClient.fetchCall(
PATH + "/api/v1/workspace/" + workspaceId + "/integrations", "/api/v1/workspace/" + workspaceId + "/integrations",
{ {
method: "GET", method: "GET",
headers: { headers: {
@@ -1,20 +1,18 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
/** /**
* This route lets us get info about a certain org * This route lets us get info about a certain org
* @param {*} req * @param {string} orgId - the organization ID
* @param {*} res
* @returns * @returns
*/ */
const getOrganization = (req, res) => { const getOrganization = ({ orgId }: { orgId: string; }) => {
return SecurityClient.fetchCall(PATH + "/api/v1/organization/" + req.orgId, { return SecurityClient.fetchCall("/api/v1/organization/" + orgId, {
method: "GET", method: "GET",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}).then(async (res) => { }).then(async (res) => {
if (res.status == 200) { if (res?.status == 200) {
return (await res.json()).organization; return (await res.json()).organization;
} else { } else {
console.log("Failed to get org info"); console.log("Failed to get org info");
@@ -1,4 +1,3 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
/** /**
@@ -9,7 +8,7 @@ import SecurityClient from "~/utilities/SecurityClient";
*/ */
const getOrganizationProjects = (req, res) => { const getOrganizationProjects = (req, res) => {
return SecurityClient.fetchCall( return SecurityClient.fetchCall(
PATH + "/api/organization/" + req.orgId + "/workspaces", "/api/organization/" + req.orgId + "/workspaces",
{ {
method: "GET", method: "GET",
headers: { headers: {
@@ -1,4 +1,3 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
/** /**
@@ -9,7 +8,7 @@ import SecurityClient from "~/utilities/SecurityClient";
*/ */
const getOrganizationSubscriptions = (req, res) => { const getOrganizationSubscriptions = (req, res) => {
return SecurityClient.fetchCall( return SecurityClient.fetchCall(
PATH + "/api/v1/organization/" + req.orgId + "/subscriptions", "/api/v1/organization/" + req.orgId + "/subscriptions",
{ {
method: "GET", method: "GET",
headers: { headers: {
@@ -1,4 +1,3 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
/** /**
@@ -9,7 +8,7 @@ import SecurityClient from "~/utilities/SecurityClient";
*/ */
const getOrganizationUserProjects = (req, res) => { const getOrganizationUserProjects = (req, res) => {
return SecurityClient.fetchCall( return SecurityClient.fetchCall(
PATH + "/api/v1/organization/" + req.orgId + "/my-workspaces", "/api/v1/organization/" + req.orgId + "/my-workspaces",
{ {
method: "GET", method: "GET",
headers: { headers: {
@@ -1,15 +1,14 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
/** /**
* This route lets us get all the users in an org. * This route lets us get all the users in an org.
* @param {*} req * @param {object} obj
* @param {*} res * @param {string} obj.orgId - organization Id
* @returns * @returns
*/ */
const getOrganizationUsers = (req, res) => { const getOrganizationUsers = ({ orgId }: { orgId: string; }) => {
return SecurityClient.fetchCall( return SecurityClient.fetchCall(
PATH + "/api/v1/organization/" + req.orgId + "/users", "/api/v1/organization/" + orgId + "/users",
{ {
method: "GET", method: "GET",
headers: { headers: {
@@ -17,7 +16,7 @@ const getOrganizationUsers = (req, res) => {
}, },
} }
).then(async (res) => { ).then(async (res) => {
if (res.status == 200) { if (res?.status == 200) {
return (await res.json()).users; return (await res.json()).users;
} else { } else {
console.log("Failed to get org users"); console.log("Failed to get org users");
@@ -1,4 +1,3 @@
import { PATH } from "~/const";
import SecurityClient from "~/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
/** /**
@@ -9,7 +8,7 @@ import SecurityClient from "~/utilities/SecurityClient";
*/ */
const StripeRedirect = ({ orgId }) => { const StripeRedirect = ({ orgId }) => {
return SecurityClient.fetchCall( return SecurityClient.fetchCall(
PATH + "/api/v1/organization/" + orgId + "/customer-portal-session", "/api/v1/organization/" + orgId + "/customer-portal-session",
{ {
method: "POST", method: "POST",
headers: { headers: {

Some files were not shown because too many files have changed in this diff Show More