mirror of
https://github.com/Infisical/infisical.git
synced 2025-03-25 14:05:03 +00:00
Merge branch 'main' into feat-error-notifs
This commit is contained in:
22
.github/workflows/helm_chart_release.yml
vendored
Normal file
22
.github/workflows/helm_chart_release.yml
vendored
Normal 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 }}
|
2
.github/workflows/release_build.yml
vendored
2
.github/workflows/release_build.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: goreleaser
|
||||
name: Go releaser
|
||||
|
||||
on:
|
||||
push:
|
||||
|
@ -106,7 +106,7 @@ const initializeDefaultOrg = async ({
|
||||
|
||||
// initialize a default workspace inside the new organization
|
||||
const workspace = await createWorkspace({
|
||||
name: `${user.firstName}'s Project`,
|
||||
name: `Example Project`,
|
||||
organizationId: organization._id.toString()
|
||||
});
|
||||
|
||||
|
@ -15,7 +15,7 @@ var rootCmd = &cobra.Command{
|
||||
Short: "Infisical CLI is used to inject environment variables into any process",
|
||||
Long: `Infisical is a simple, end-to-end encrypted service that enables teams to sync and manage their environment variables across their development life cycle.`,
|
||||
CompletionOptions: cobra.CompletionOptions{DisableDefaultCmd: true},
|
||||
Version: "0.1.5",
|
||||
Version: "0.1.6",
|
||||
}
|
||||
|
||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||
|
16
docs/contributing/FAQ.mdx
Normal file
16
docs/contributing/FAQ.mdx
Normal 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`.
|
@ -102,8 +102,11 @@
|
||||
"pages": [
|
||||
"self-hosting/overview",
|
||||
{
|
||||
"group": "Deployments",
|
||||
"pages": ["self-hosting/deployments/linux"]
|
||||
"group": "Deployments options",
|
||||
"pages": [
|
||||
"self-hosting/deployments/linux",
|
||||
"self-hosting/deployments/kubernetes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Configuration",
|
||||
@ -162,7 +165,8 @@
|
||||
"pages": [
|
||||
"contributing/overview",
|
||||
"contributing/code-of-conduct",
|
||||
"contributing/developing"
|
||||
"contributing/developing",
|
||||
"contributing/FAQ"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
54
docs/self-hosting/deployments/kubernetes.mdx
Normal file
54
docs/self-hosting/deployments/kubernetes.mdx
Normal file
@ -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.
|
@ -9,17 +9,22 @@ Self-hosting Infisical means managing the service yourself, taking care of upgra
|
||||
|
||||
If you're less technical and looking for a hands-free experience with minimal overhead then we recommend Infisical Cloud.
|
||||
|
||||
Infisical Cloud also comes with some extra features unavailabe in the self-hosted edition. You can find more information about Infisical Cloud's offering on the pricing page.
|
||||
Infisical Cloud also comes with some extra features unavailable in the self-hosted edition. You can find more information about Infisical Cloud's offering on the pricing page.
|
||||
|
||||
</Info>
|
||||
|
||||
## Deployment options
|
||||
|
||||
Infisical can be deployed on a Linux VM with docker-compose. We're rolling out more specific deployment options for DigitalOcean, AWS, GCP, and Azure soon.
|
||||
Infisical can be deployed on a Linux VM with docker-compose and Kubernetes. We're rolling out more specific deployment options for DigitalOcean, AWS, GCP, and Azure soon.
|
||||
|
||||
Options:
|
||||
|
||||
- [Linux VM](/self-hosting/deployments/linux)
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Any Linux" icon="square-1" color="#ea5a0c" href="/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
|
||||
|
||||
|
@ -10,7 +10,12 @@
|
||||
"rules": {
|
||||
"react-hooks/exhaustive-deps": "off",
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-unused-vars": "error",
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"@typescript-eslint/no-empty-function": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
|
||||
"simple-import-sort/exports": "warn",
|
||||
"simple-import-sort/imports": [
|
||||
"warn",
|
||||
|
@ -1,19 +1,27 @@
|
||||
import React from "react";
|
||||
import { useState } from "react";
|
||||
import React, { useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import {
|
||||
faCircle,
|
||||
faCircleExclamation,
|
||||
faE,
|
||||
faEye,
|
||||
faEyeSlash,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { faCircle, faEye, faEyeSlash } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import guidGenerator from "../utilities/randomId";
|
||||
import Error from "./Error";
|
||||
|
||||
const InputField = (props) => {
|
||||
interface InputFieldProps {
|
||||
static?: boolean;
|
||||
label: string;
|
||||
type: string;
|
||||
value: string;
|
||||
placeholder?: string;
|
||||
isRequired: boolean;
|
||||
disabled?: boolean;
|
||||
error?: boolean;
|
||||
text?: string;
|
||||
name?: string;
|
||||
blurred?: boolean;
|
||||
errorText?: string;
|
||||
onChangeHandler: (value: string) => void;
|
||||
}
|
||||
|
||||
const InputField = (props: InputFieldProps) => {
|
||||
const [passwordVisible, setPasswordVisible] = useState(false);
|
||||
const router = useRouter();
|
||||
|
||||
@ -67,7 +75,7 @@ const InputField = (props) => {
|
||||
>
|
||||
<input
|
||||
onChange={(e) => props.onChangeHandler(e.target.value)}
|
||||
type={passwordVisible == false ? props.type : "text"}
|
||||
type={passwordVisible === false ? props.type : "text"}
|
||||
placeholder={props.placeholder}
|
||||
value={props.value}
|
||||
required={props.isRequired}
|
@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-unexpected-multiline */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { useEffect, useState } from "react";
|
||||
import Link from "next/link";
|
||||
@ -19,8 +20,10 @@ import getOrganizationUsers from "~/pages/api/organization/GetOrgUsers";
|
||||
import addUserToWorkspace from "~/pages/api/workspace/addUserToWorkspace";
|
||||
import createWorkspace from "~/pages/api/workspace/createWorkspace";
|
||||
import getWorkspaces from "~/pages/api/workspace/getWorkspaces";
|
||||
import uploadKeys from "~/pages/api/workspace/uploadKeys";
|
||||
|
||||
import NavBarDashboard from "../navigation/NavBarDashboard";
|
||||
import { tempLocalStorage } from "../utilities/checks/tempLocalStorage";
|
||||
import {
|
||||
decryptAssymmetric,
|
||||
encryptAssymmetric,
|
||||
@ -29,13 +32,17 @@ import Button from "./buttons/Button";
|
||||
import AddWorkspaceDialog from "./dialog/AddWorkspaceDialog";
|
||||
import Listbox from "./Listbox";
|
||||
|
||||
export default function Layout({ children }) {
|
||||
interface LayoutProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function Layout({ children }: LayoutProps) {
|
||||
const router = useRouter();
|
||||
const [workspaceList, setWorkspaceList] = useState([]);
|
||||
const [workspaceMapping, setWorkspaceMapping] = useState([{ 1: 2 }]);
|
||||
const [workspaceSelected, setWorkspaceSelected] = useState("∞");
|
||||
let [newWorkspaceName, setNewWorkspaceName] = useState("");
|
||||
let [isOpen, setIsOpen] = useState(false);
|
||||
const [newWorkspaceName, setNewWorkspaceName] = useState("");
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
@ -43,165 +50,186 @@ export default function Layout({ children }) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
|
||||
function openModal() {
|
||||
setIsOpen(true);
|
||||
}
|
||||
|
||||
// TODO: what to do about the fact that 2ids can have the same name
|
||||
|
||||
/**
|
||||
* When a user creates a new workspace, redirect them to the page of the new workspace.
|
||||
* @param {*} workspaceName
|
||||
*/
|
||||
async function submitModal(workspaceName, addAllUsers) {
|
||||
async function submitModal(workspaceName: string, addAllUsers: boolean) {
|
||||
setLoading(true);
|
||||
// timeout code.
|
||||
setTimeout(() => setLoading(false), 1500);
|
||||
const workspaces = await getWorkspaces();
|
||||
const currentWorkspaces = workspaces.map((workspace) => workspace.name);
|
||||
if (!currentWorkspaces.includes(workspaceName)) {
|
||||
const newWorkspace = await createWorkspace(
|
||||
workspaceName,
|
||||
localStorage.getItem("orgData.id")
|
||||
);
|
||||
let newWorkspaceId;
|
||||
try {
|
||||
newWorkspaceId = newWorkspace._id;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
if (addAllUsers) {
|
||||
let orgUsers = await getOrganizationUsers({
|
||||
orgId: localStorage.getItem("orgData.id"),
|
||||
|
||||
try {
|
||||
const workspaces = await getWorkspaces();
|
||||
const currentWorkspaces = workspaces.map((workspace) => workspace.name);
|
||||
if (!currentWorkspaces.includes(workspaceName)) {
|
||||
const newWorkspace = await createWorkspace({
|
||||
workspaceName,
|
||||
organizationId: tempLocalStorage("orgData.id"),
|
||||
});
|
||||
orgUsers.map(async (user) => {
|
||||
if (user.status == "accepted") {
|
||||
let result = await addUserToWorkspace(
|
||||
user.user.email,
|
||||
newWorkspaceId
|
||||
);
|
||||
if (result?.invitee && result?.latestKey) {
|
||||
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY");
|
||||
const newWorkspaceId = newWorkspace._id;
|
||||
|
||||
// assymmetrically decrypt symmetric key with local private key
|
||||
const key = decryptAssymmetric({
|
||||
ciphertext: result.latestKey.encryptedKey,
|
||||
nonce: result.latestKey.nonce,
|
||||
publicKey: result.latestKey.sender.publicKey,
|
||||
privateKey: PRIVATE_KEY,
|
||||
});
|
||||
if (addAllUsers) {
|
||||
const orgUsers = await getOrganizationUsers({
|
||||
orgId: tempLocalStorage("orgData.id"),
|
||||
});
|
||||
orgUsers.map(async (user: any) => {
|
||||
if (user.status == "accepted") {
|
||||
const result = await addUserToWorkspace(
|
||||
user.user.email,
|
||||
newWorkspaceId
|
||||
);
|
||||
if (result?.invitee && result?.latestKey) {
|
||||
const PRIVATE_KEY = tempLocalStorage("PRIVATE_KEY");
|
||||
|
||||
const { ciphertext, nonce } = encryptAssymmetric({
|
||||
plaintext: key,
|
||||
publicKey: result.invitee.publicKey,
|
||||
privateKey: PRIVATE_KEY,
|
||||
});
|
||||
// assymmetrically decrypt symmetric key with local private key
|
||||
const key = decryptAssymmetric({
|
||||
ciphertext: result.latestKey.encryptedKey,
|
||||
nonce: result.latestKey.nonce,
|
||||
publicKey: result.latestKey.sender.publicKey,
|
||||
privateKey: PRIVATE_KEY,
|
||||
});
|
||||
|
||||
uploadKeys(newWorkspaceId, result.invitee._id, ciphertext, nonce);
|
||||
const { ciphertext, nonce } = encryptAssymmetric({
|
||||
plaintext: key,
|
||||
publicKey: result.invitee.publicKey,
|
||||
privateKey: PRIVATE_KEY,
|
||||
}) as { ciphertext: string; nonce: string };
|
||||
|
||||
uploadKeys(
|
||||
newWorkspaceId,
|
||||
result.invitee._id,
|
||||
ciphertext,
|
||||
nonce
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
router.push("/dashboard/" + newWorkspaceId + "?Development");
|
||||
setIsOpen(false);
|
||||
setNewWorkspaceName("");
|
||||
} else {
|
||||
console.error("A project with this name already exists.");
|
||||
setError(true);
|
||||
setLoading(false);
|
||||
}
|
||||
router.push("/dashboard/" + newWorkspaceId + "?Development");
|
||||
setIsOpen(false);
|
||||
setNewWorkspaceName("");
|
||||
} else {
|
||||
setError("A project with this name already exists.");
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
setError(true);
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
function openModal() {
|
||||
setIsOpen(true);
|
||||
}
|
||||
|
||||
const menuItems = [
|
||||
{
|
||||
href:
|
||||
"/dashboard/" + workspaceMapping[workspaceSelected] + "?Development",
|
||||
"/dashboard/" +
|
||||
workspaceMapping[workspaceSelected as any] +
|
||||
"?Development",
|
||||
title: "Secrets",
|
||||
emoji: <FontAwesomeIcon icon={faKey} />,
|
||||
},
|
||||
{
|
||||
href: "/users/" + workspaceMapping[workspaceSelected],
|
||||
href: "/users/" + workspaceMapping[workspaceSelected as any],
|
||||
title: "Members",
|
||||
emoji: <FontAwesomeIcon icon={faUser} />,
|
||||
},
|
||||
{
|
||||
href: "/integrations/" + workspaceMapping[workspaceSelected],
|
||||
href: "/integrations/" + workspaceMapping[workspaceSelected as any],
|
||||
title: "Integrations",
|
||||
emoji: <FontAwesomeIcon icon={faPlug} />,
|
||||
},
|
||||
{
|
||||
href: "/settings/project/" + workspaceMapping[workspaceSelected],
|
||||
href: "/settings/project/" + workspaceMapping[workspaceSelected as any],
|
||||
title: "Project Settings",
|
||||
emoji: <FontAwesomeIcon icon={faGear} />,
|
||||
},
|
||||
];
|
||||
|
||||
useEffect(async () => {
|
||||
useEffect(() => {
|
||||
// Put a user in a workspace if they're not in one yet
|
||||
if (
|
||||
localStorage.getItem("orgData.id") == null ||
|
||||
localStorage.getItem("orgData.id") == ""
|
||||
) {
|
||||
const userOrgs = await getOrganizations();
|
||||
localStorage.setItem("orgData.id", userOrgs[0]._id);
|
||||
}
|
||||
|
||||
let orgUserProjects = await getOrganizationUserProjects({
|
||||
orgId: localStorage.getItem("orgData.id"),
|
||||
});
|
||||
let userWorkspaces = orgUserProjects;
|
||||
if (
|
||||
userWorkspaces.length == 0 &&
|
||||
router.asPath != "/noprojects" &&
|
||||
!router.asPath.includes("settings")
|
||||
) {
|
||||
router.push("/noprojects");
|
||||
} else if (router.asPath != "/noprojects") {
|
||||
const intendedWorkspaceId = router.asPath
|
||||
.split("/")
|
||||
[router.asPath.split("/").length - 1].split("?")[0];
|
||||
|
||||
// If a user is not a member of a workspace they are trying to access, just push them to one of theirs
|
||||
if (
|
||||
intendedWorkspaceId != "heroku" &&
|
||||
!userWorkspaces
|
||||
.map((workspace) => workspace._id)
|
||||
.includes(intendedWorkspaceId)
|
||||
) {
|
||||
router.push("/dashboard/" + userWorkspaces[0]._id + "?Development");
|
||||
} else {
|
||||
setWorkspaceList(userWorkspaces.map((workspace) => workspace.name));
|
||||
setWorkspaceMapping(
|
||||
Object.fromEntries(
|
||||
userWorkspaces.map((workspace) => [workspace.name, workspace._id])
|
||||
)
|
||||
);
|
||||
setWorkspaceSelected(
|
||||
Object.fromEntries(
|
||||
userWorkspaces.map((workspace) => [workspace._id, workspace.name])
|
||||
)[
|
||||
router.asPath
|
||||
.split("/")
|
||||
[router.asPath.split("/").length - 1].split("?")[0]
|
||||
]
|
||||
);
|
||||
const putUserInWorkSpace = async () => {
|
||||
if (tempLocalStorage("orgData.id") === "") {
|
||||
const userOrgs = await getOrganizations();
|
||||
localStorage.setItem("orgData.id", userOrgs[0]._id);
|
||||
}
|
||||
}
|
||||
|
||||
const orgUserProjects = await getOrganizationUserProjects({
|
||||
orgId: tempLocalStorage("orgData.id"),
|
||||
});
|
||||
const userWorkspaces = orgUserProjects;
|
||||
if (
|
||||
userWorkspaces.length == 0 &&
|
||||
router.asPath != "/noprojects" &&
|
||||
!router.asPath.includes("settings")
|
||||
) {
|
||||
router.push("/noprojects");
|
||||
} else if (router.asPath != "/noprojects") {
|
||||
const intendedWorkspaceId = router.asPath
|
||||
.split("/")
|
||||
[router.asPath.split("/").length - 1].split("?")[0];
|
||||
// If a user is not a member of a workspace they are trying to access, just push them to one of theirs
|
||||
if (
|
||||
intendedWorkspaceId != "heroku" &&
|
||||
!userWorkspaces
|
||||
.map((workspace: { _id: string }) => workspace._id)
|
||||
.includes(intendedWorkspaceId)
|
||||
) {
|
||||
router.push("/dashboard/" + userWorkspaces[0]._id + "?Development");
|
||||
} else {
|
||||
setWorkspaceList(
|
||||
userWorkspaces.map((workspace: any) => workspace.name)
|
||||
);
|
||||
setWorkspaceMapping(
|
||||
Object.fromEntries(
|
||||
userWorkspaces.map((workspace: any) => [
|
||||
workspace.name,
|
||||
workspace._id,
|
||||
])
|
||||
) as any
|
||||
);
|
||||
setWorkspaceSelected(
|
||||
Object.fromEntries(
|
||||
userWorkspaces.map((workspace: any) => [
|
||||
workspace._id,
|
||||
workspace.name,
|
||||
])
|
||||
)[
|
||||
router.asPath
|
||||
.split("/")
|
||||
[router.asPath.split("/").length - 1].split("?")[0]
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
putUserInWorkSpace();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
if (
|
||||
workspaceMapping[workspaceSelected] &&
|
||||
workspaceMapping[workspaceSelected] !==
|
||||
workspaceMapping[Number(workspaceSelected)] &&
|
||||
`${workspaceMapping[Number(workspaceSelected)]}` !==
|
||||
router.asPath
|
||||
.split("/")
|
||||
[router.asPath.split("/").length - 1].split("?")[0]
|
||||
) {
|
||||
router.push(
|
||||
"/dashboard/" + workspaceMapping[workspaceSelected] + "?Development"
|
||||
"/dashboard/" +
|
||||
workspaceMapping[Number(workspaceSelected)] +
|
||||
"?Development"
|
||||
);
|
||||
localStorage.setItem(
|
||||
"projectData.id",
|
||||
workspaceMapping[workspaceSelected]
|
||||
`${workspaceMapping[Number(workspaceSelected)]}`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
@ -218,18 +246,18 @@ export default function Layout({ children }) {
|
||||
<nav className="flex flex-col justify-between items-between h-full">
|
||||
{/* <div className="py-6"></div> */}
|
||||
<div>
|
||||
<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">
|
||||
<div className="flex justify-center w-full mt-[4.5rem] mb-6 bg-bunker-600 h-20 flex-col items-center px-4">
|
||||
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
|
||||
PROJECT
|
||||
</div>
|
||||
{workspaceList.length > 0 ? (
|
||||
<Listbox
|
||||
selected={workspaceSelected}
|
||||
onChange={setWorkspaceSelected}
|
||||
onChange={setWorkspaceSelected as any}
|
||||
data={workspaceList}
|
||||
buttonAction={openModal}
|
||||
text=""
|
||||
workspaceMapping={workspaceMapping}
|
||||
// workspaceMapping={workspaceMapping as any}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
@ -255,12 +283,18 @@ export default function Layout({ children }) {
|
||||
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">{emoji}</p>
|
||||
<p className="w-6 ml-4 mr-2 flex items-center justify-center text-lg">
|
||||
{emoji}
|
||||
</p>
|
||||
{title}
|
||||
</div>
|
||||
) : router.asPath == "/noprojects" ? (
|
||||
<div className={`flex p-2.5 text-white text-sm rounded`}>
|
||||
<p className="w-10 flex items-center justify-center text-lg">{emoji}</p>
|
||||
<div
|
||||
className={`flex p-2.5 text-white text-sm rounded`}
|
||||
>
|
||||
<p className="w-10 flex items-center justify-center text-lg">
|
||||
{emoji}
|
||||
</p>
|
||||
{title}
|
||||
</div>
|
||||
) : (
|
||||
@ -268,7 +302,9 @@ export default function Layout({ children }) {
|
||||
<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>
|
||||
<p className="w-10 flex items-center justify-center text-lg">
|
||||
{emoji}
|
||||
</p>
|
||||
{title}
|
||||
</div>
|
||||
</Link>
|
||||
@ -283,15 +319,21 @@ export default function Layout({ children }) {
|
||||
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>
|
||||
<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]}>
|
||||
<Link
|
||||
href={`/home/` + workspaceMapping[workspaceSelected as any]}
|
||||
>
|
||||
<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>
|
||||
<p className="w-10 flex items-center justify-center text-lg">
|
||||
<FontAwesomeIcon icon={faBookOpen} />
|
||||
</p>
|
||||
Infisical Guide
|
||||
</div>
|
||||
</Link>
|
||||
@ -311,7 +353,7 @@ export default function Layout({ children }) {
|
||||
<main className="flex-1 bg-bunker-800">{children}</main>
|
||||
</div>
|
||||
</div>
|
||||
<div className="block md:hidden bg-bunker-800 w-screen h-screen flex flex-col justify-center items-center">
|
||||
<div className="md:hidden bg-bunker-800 w-screen h-screen flex flex-col justify-center items-center">
|
||||
<FontAwesomeIcon
|
||||
icon={faMobile}
|
||||
className="text-gray-300 text-7xl mb-8"
|
@ -8,14 +8,13 @@ import {
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Listbox, Transition } from "@headlessui/react";
|
||||
|
||||
|
||||
type ListBoxProps = {
|
||||
selected: string,
|
||||
onChange: () => void,
|
||||
data: string[],
|
||||
text: string,
|
||||
buttonAction: () => void,
|
||||
width: string,
|
||||
interface ListBoxProps {
|
||||
selected: string;
|
||||
onChange: () => void;
|
||||
data: string[];
|
||||
text: string;
|
||||
buttonAction: () => void;
|
||||
isFull?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,14 +34,14 @@ export default function ListBox({
|
||||
data,
|
||||
text,
|
||||
buttonAction,
|
||||
width,
|
||||
} : ListBoxProps): JSX.Element {
|
||||
isFull,
|
||||
}: ListBoxProps): JSX.Element {
|
||||
return (
|
||||
<Listbox value={selected} onChange={onChange}>
|
||||
<div className="relative">
|
||||
<Listbox.Button
|
||||
className={`text-gray-400 relative ${
|
||||
width == "full" ? "w-full" : "w-52"
|
||||
isFull ? "w-full" : "w-52"
|
||||
} cursor-default rounded-md bg-white/[0.07] hover:bg-white/[0.11] duration-200 py-2.5 pl-3 pr-10 text-left shadow-md focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm`}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
|
@ -6,13 +6,13 @@ import {
|
||||
FontAwesomeIconProps,
|
||||
} from "@fortawesome/react-fontawesome";
|
||||
|
||||
var classNames = require("classnames");
|
||||
const classNames = require("classnames");
|
||||
|
||||
type ButtonProps = {
|
||||
text: string;
|
||||
onButtonPressed: () => void;
|
||||
loading?: boolean;
|
||||
color: string;
|
||||
color?: string;
|
||||
size: string;
|
||||
icon?: IconProp;
|
||||
active?: boolean;
|
||||
@ -40,7 +40,7 @@ export default function Button(props: ButtonProps): JSX.Element {
|
||||
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",
|
||||
|
||||
// Setting background colors and hover modes
|
||||
@ -67,7 +67,7 @@ export default function Button(props: ButtonProps): JSX.Element {
|
||||
props.size == "icon-sm" && "h-9 w-9 flex items-center justify-center"
|
||||
);
|
||||
|
||||
let styleMainDiv = classNames(
|
||||
const styleMainDiv = classNames(
|
||||
"relative font-medium flex items-center",
|
||||
|
||||
// Setting the text color for the text and icon
|
||||
@ -79,11 +79,11 @@ export default function Button(props: ButtonProps): JSX.Element {
|
||||
props.size == "icon" && "flex items-center justify-center"
|
||||
);
|
||||
|
||||
let textStyle = classNames(
|
||||
const textStyle = classNames(
|
||||
"relative duration-200",
|
||||
|
||||
// Show the loading sign if the loading indicator is on
|
||||
Boolean(props.loading) ? "opacity-0" : "opacity-100",
|
||||
props.loading ? "opacity-0" : "opacity-100",
|
||||
props.size == "md" && "text-sm",
|
||||
props.size == "lg" && "text-lg"
|
||||
);
|
||||
|
@ -37,7 +37,7 @@ const AddServiceTokenDialog = ({
|
||||
const [serviceTokenCopied, setServiceTokenCopied] = useState(false);
|
||||
|
||||
const generateServiceToken = async () => {
|
||||
const latestFileKey = await getLatestFileKey(workspaceId);
|
||||
const latestFileKey = await getLatestFileKey({ workspaceId });
|
||||
|
||||
const key = decryptAssymmetric({
|
||||
ciphertext: latestFileKey.latestKey.encryptedKey,
|
||||
|
@ -88,7 +88,7 @@ const UserTable = ({
|
||||
}, [userData, myUser]);
|
||||
|
||||
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");
|
||||
|
||||
|
@ -47,27 +47,44 @@ const supportOptions = [
|
||||
],
|
||||
];
|
||||
|
||||
export default function Navbar({ onButtonPressed }) {
|
||||
const router = useRouter();
|
||||
const [user, setUser] = useState({});
|
||||
const [orgs, setOrgs] = useState([]);
|
||||
const [currentOrg, setCurrentOrg] = useState([]);
|
||||
export interface ICurrentOrg {
|
||||
name: string;
|
||||
}
|
||||
|
||||
useEffect(async () => {
|
||||
const userData = await getUser();
|
||||
setUser(userData);
|
||||
const orgsData = await getOrganizations();
|
||||
setOrgs(orgsData);
|
||||
const currentOrg = await getOrganization({
|
||||
orgId: localStorage.getItem("orgData.id"),
|
||||
});
|
||||
setCurrentOrg(currentOrg);
|
||||
export interface IUser {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the navigation bar in the main app.
|
||||
* It has two main components: support options and user menu (inlcudes billing, logout, org/user settings)
|
||||
* @returns NavBar
|
||||
*/
|
||||
export default function Navbar() {
|
||||
const router = useRouter();
|
||||
const [user, setUser] = useState<IUser | undefined>();
|
||||
const [orgs, setOrgs] = useState([]);
|
||||
const [currentOrg, setCurrentOrg] = useState<ICurrentOrg | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const userData = await getUser();
|
||||
setUser(userData);
|
||||
const orgsData = await getOrganizations();
|
||||
setOrgs(orgsData);
|
||||
const currentOrg = await getOrganization({
|
||||
orgId: String(localStorage.getItem("orgData.id")),
|
||||
});
|
||||
setCurrentOrg(currentOrg);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
const closeApp = async () => {
|
||||
console.log("Logging out...");
|
||||
await logout();
|
||||
router.push("/");
|
||||
router.push("/login");
|
||||
};
|
||||
|
||||
return (
|
||||
@ -108,7 +125,7 @@ export default function Navbar({ onButtonPressed }) {
|
||||
key={guidGenerator()}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
href={option[2]}
|
||||
href={String(option[2])}
|
||||
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">
|
||||
@ -238,9 +255,9 @@ export default function Navbar({ onButtonPressed }) {
|
||||
<div className="flex flex-col items-start px-1 mt-3 mb-2">
|
||||
{orgs
|
||||
.filter(
|
||||
(org) => org._id != localStorage.getItem("orgData.id")
|
||||
(org : { _id: string }) => org._id != localStorage.getItem("orgData.id")
|
||||
)
|
||||
.map((org) => (
|
||||
.map((org : { _id: string; name: string; }) => (
|
||||
<div
|
||||
key={guidGenerator()}
|
||||
onClick={() => {
|
@ -1,29 +1,37 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { faCcMastercard, faCcVisa } from "@fortawesome/free-brands-svg-icons";
|
||||
import {
|
||||
faAngleRight,
|
||||
faQuestionCircle,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { faCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import getOrganization from "~/pages/api/organization/GetOrg";
|
||||
import getWorkspaceInfo from "~/pages/api/workspace/getWorkspaceInfo";
|
||||
import getProjectInfo from "~/pages/api/workspace/getProjectInfo";
|
||||
|
||||
export default function NavHeader({ pageName, isProjectRelated }) {
|
||||
/**
|
||||
* This is the component at the top of almost every page.
|
||||
* It shows how to navigate to a certain page.
|
||||
* It future these links should also be clickable and hoverable
|
||||
* @param obj
|
||||
* @param obj.pageName - Name of the page
|
||||
* @param obj.isProjectRelated - whether this page is related to project or now (determine if it's 2 or 3 navigation steps)
|
||||
* @returns
|
||||
*/
|
||||
export default function NavHeader({ pageName, isProjectRelated } : { pageName: string; isProjectRelated: boolean; }): JSX.Element {
|
||||
const [orgName, setOrgName] = useState("");
|
||||
const [workspaceName, setWorkspaceName] = useState("");
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
let org = await getOrganization({
|
||||
orgId: localStorage.getItem("orgData.id"),
|
||||
const orgId = localStorage.getItem("orgData.id")
|
||||
const org = await getOrganization({
|
||||
orgId: orgId ? orgId : "",
|
||||
});
|
||||
setOrgName(org.name);
|
||||
let workspace = await getWorkspaceInfo({
|
||||
workspaceId: router.query.id,
|
||||
|
||||
const workspace = await getProjectInfo({
|
||||
projectId: String(router.query.id),
|
||||
});
|
||||
setWorkspaceName(workspace.name);
|
||||
})();
|
@ -3,7 +3,7 @@ import token from "~/pages/api/auth/Token";
|
||||
export default class SecurityClient {
|
||||
static #token = "";
|
||||
|
||||
contructor() {}
|
||||
constructor() {}
|
||||
|
||||
static setToken(token) {
|
||||
this.#token = token;
|
||||
|
@ -32,7 +32,6 @@ const attemptLogin = async (
|
||||
isLogin
|
||||
) => {
|
||||
try {
|
||||
let userWorkspace, userOrg;
|
||||
client.init(
|
||||
{
|
||||
username: email,
|
||||
@ -43,7 +42,7 @@ const attemptLogin = async (
|
||||
|
||||
let serverPublicKey, salt;
|
||||
try {
|
||||
const res = await login1(email, clientPublicKey);
|
||||
let res = await login1(email, clientPublicKey);
|
||||
res = await res.json();
|
||||
serverPublicKey = res.serverPublicKey;
|
||||
salt = res.salt;
|
||||
@ -134,20 +133,14 @@ const attemptLogin = async (
|
||||
|
||||
// If user is logging in for the first time, add the example keys
|
||||
if (isSignUp) {
|
||||
await pushKeys(
|
||||
{
|
||||
await pushKeys({
|
||||
obj: {
|
||||
DATABASE_URL: [
|
||||
"mongodb+srv://${DB_USERNAME}:${DB_PASSWORD}@mongodb.net",
|
||||
"personal",
|
||||
],
|
||||
DB_USERNAME: [
|
||||
"user1234",
|
||||
"personal",
|
||||
],
|
||||
DB_PASSWORD: [
|
||||
"ah8jak3hk8dhiu4dw7whxwe1l",
|
||||
"personal",
|
||||
],
|
||||
DB_USERNAME: ["user1234", "personal"],
|
||||
DB_PASSWORD: ["ah8jak3hk8dhiu4dw7whxwe1l", "personal"],
|
||||
TWILIO_AUTH_TOKEN: [
|
||||
"hgSIwDAKvz8PJfkj6xkzYqzGmAP3HLuG",
|
||||
"shared",
|
||||
@ -155,9 +148,9 @@ const attemptLogin = async (
|
||||
WEBSITE_URL: ["http://localhost:3000", "shared"],
|
||||
STRIPE_SECRET_KEY: ["sk_test_7348oyho4hfq398HIUOH78", "shared"],
|
||||
},
|
||||
projectToLogin,
|
||||
"Development"
|
||||
);
|
||||
workspaceId: projectToLogin,
|
||||
env: "Development"
|
||||
});
|
||||
}
|
||||
try {
|
||||
if (email) {
|
||||
|
@ -1,18 +1,21 @@
|
||||
interface PasswordCheckProps {
|
||||
password: string;
|
||||
currentErrorCheck: boolean;
|
||||
setPasswordErrorLength: (value: boolean) => void;
|
||||
setPasswordErrorNumber: (value: boolean) => void;
|
||||
setPasswordErrorLowerCase: (value: boolean) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function checks a user password with respect to some criteria.
|
||||
* @param {*} password
|
||||
* @param {*} setPasswordError
|
||||
* @param {*} setPasswordErrorMessage
|
||||
* @param {*} currentErrorCheck
|
||||
* @returns
|
||||
*/
|
||||
const passwordCheck = (
|
||||
const passwordCheck = ({
|
||||
password,
|
||||
setPasswordErrorLength,
|
||||
setPasswordErrorNumber,
|
||||
setPasswordErrorLowerCase,
|
||||
currentErrorCheck
|
||||
) => {
|
||||
currentErrorCheck,
|
||||
}: PasswordCheckProps) => {
|
||||
let errorCheck = currentErrorCheck;
|
||||
if (!password || password.length < 14) {
|
||||
setPasswordErrorLength(true);
|
11
frontend/components/utilities/checks/tempLocalStorage.ts
Normal file
11
frontend/components/utilities/checks/tempLocalStorage.ts
Normal file
@ -0,0 +1,11 @@
|
||||
// this is temporary util function. create error handling logic for localStorage and delete this.
|
||||
export const tempLocalStorage = (key: string) => {
|
||||
const value = localStorage.getItem(key);
|
||||
|
||||
if (value === null || value === "") {
|
||||
console.warn("No value found in localStorage for key");
|
||||
return "";
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
@ -108,11 +108,7 @@ const changePassword = async (
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(
|
||||
"Something went wrong during changing the password",
|
||||
slat,
|
||||
serverPublicKey
|
||||
);
|
||||
console.log("Something went wrong during changing the password");
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
@ -7,20 +7,30 @@ import { envMapping } from "../../../public/data/frequentConstants";
|
||||
const crypto = require("crypto");
|
||||
const {
|
||||
decryptAssymmetric,
|
||||
decryptSymmetric,
|
||||
encryptSymmetric,
|
||||
encryptAssymmetric,
|
||||
} = require("../cryptography/crypto");
|
||||
const nacl = require("tweetnacl");
|
||||
nacl.util = require("tweetnacl-util");
|
||||
|
||||
export interface IK {
|
||||
publicKey: string;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
const pushKeys = async (obj, workspaceId, env) => {
|
||||
let sharedKey = await getLatestFileKey(workspaceId);
|
||||
/**
|
||||
* This function pushes the keys to the database after decrypting them end-to-end
|
||||
* @param {object} obj
|
||||
* @param {object} obj.obj - object with all the key pairs
|
||||
* @param {object} obj.workspaceId - the id of a project to which a user is pushing
|
||||
* @param {object} obj.env - which environment a user is pushing to
|
||||
*/
|
||||
const pushKeys = async({ obj, workspaceId, env }: { obj: object; workspaceId: string; env: string; }) => {
|
||||
const sharedKey = await getLatestFileKey({ workspaceId });
|
||||
|
||||
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY");
|
||||
|
||||
let randomBytes;
|
||||
let randomBytes: string;
|
||||
if (Object.keys(sharedKey).length > 0) {
|
||||
// case: a (shared) key exists for the workspace
|
||||
randomBytes = decryptAssymmetric({
|
||||
@ -51,11 +61,11 @@ const pushKeys = async (obj, workspaceId, env) => {
|
||||
iv: ivValue,
|
||||
tag: tagValue,
|
||||
} = encryptSymmetric({
|
||||
plaintext: obj[key][0],
|
||||
plaintext: obj[key as keyof typeof obj][0],
|
||||
key: randomBytes,
|
||||
});
|
||||
|
||||
const visibility = obj[key][1] != null ? obj[key][1] : "personal";
|
||||
const visibility = obj[key as keyof typeof obj][1] != null ? obj[key as keyof typeof obj][1] : "personal";
|
||||
|
||||
return {
|
||||
ciphertextKey,
|
||||
@ -65,7 +75,7 @@ const pushKeys = async (obj, workspaceId, env) => {
|
||||
ciphertextValue,
|
||||
ivValue,
|
||||
tagValue,
|
||||
hashValue: crypto.createHash("sha256").update(obj[key][0]).digest("hex"),
|
||||
hashValue: crypto.createHash("sha256").update(obj[key as keyof typeof obj][0]).digest("hex"),
|
||||
type: visibility,
|
||||
};
|
||||
});
|
||||
@ -76,7 +86,7 @@ const pushKeys = async (obj, workspaceId, env) => {
|
||||
});
|
||||
|
||||
// assymmetrically encrypt key with each receiver public keys
|
||||
const keys = publicKeys.map((k) => {
|
||||
const keys = publicKeys.map((k: IK) => {
|
||||
const { ciphertext, nonce } = encryptAssymmetric({
|
||||
plaintext: randomBytes,
|
||||
publicKey: k.publicKey,
|
||||
@ -95,7 +105,7 @@ const pushKeys = async (obj, workspaceId, env) => {
|
||||
workspaceId,
|
||||
secrets,
|
||||
keys,
|
||||
environment: envMapping[env],
|
||||
environment: envMapping[env as keyof typeof envMapping],
|
||||
});
|
||||
};
|
||||
|
@ -31,8 +31,8 @@ const issueBackupPrivateKey = ({
|
||||
if (res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
return res;
|
||||
console.log("Failed to issue the backup key");
|
||||
return res;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -3,11 +3,8 @@ import SecurityClient from "~/utilities/SecurityClient";
|
||||
/**
|
||||
* This route logs the user out. Note: the user should authorized to do this.
|
||||
* We first try to log out - if the authorization fails (response.status = 401), we refetch the new token, and then retry
|
||||
* @param {*} req
|
||||
* @param {*} res
|
||||
* @returns
|
||||
*/
|
||||
const logout = async (req, res) => {
|
||||
const logout = async () => {
|
||||
return SecurityClient.fetchCall("/api/v1/auth/logout", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
@ -15,7 +12,7 @@ const logout = async (req, res) => {
|
||||
},
|
||||
credentials: "include",
|
||||
}).then((res) => {
|
||||
if (res.status == 200) {
|
||||
if (res?.status == 200) {
|
||||
SecurityClient.setToken("");
|
||||
// Delete the cookie by not setting a value; Alternatively clear the local storage
|
||||
localStorage.setItem("publicKey", "");
|
@ -2,18 +2,17 @@ import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
/**
|
||||
* This route lets us get info about a certain org
|
||||
* @param {*} req
|
||||
* @param {*} res
|
||||
* @param {string} orgId - the organization ID
|
||||
* @returns
|
||||
*/
|
||||
const getOrganization = (req, res) => {
|
||||
return SecurityClient.fetchCall("/api/v1/organization/" + req.orgId, {
|
||||
const getOrganization = ({ orgId }: { orgId: string; }) => {
|
||||
return SecurityClient.fetchCall("/api/v1/organization/" + orgId, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
if (res?.status == 200) {
|
||||
return (await res.json()).organization;
|
||||
} else {
|
||||
console.log("Failed to get org info");
|
@ -6,7 +6,7 @@ import SecurityClient from "~/utilities/SecurityClient";
|
||||
* @param {*} res
|
||||
* @returns
|
||||
*/
|
||||
const getOrganizationUserProjects = (req, res) => {
|
||||
const getOrganizationUserProjects = (req) => {
|
||||
return SecurityClient.fetchCall(
|
||||
"/api/v1/organization/" + req.orgId + "/my-workspaces",
|
||||
{
|
||||
|
@ -6,7 +6,7 @@ import SecurityClient from "~/utilities/SecurityClient";
|
||||
* @param {string} obj.orgId - organization Id
|
||||
* @returns
|
||||
*/
|
||||
const getOrganizationUsers = ({ orgId }) => {
|
||||
const getOrganizationUsers = ({ orgId }: { orgId: string; }) => {
|
||||
return SecurityClient.fetchCall(
|
||||
"/api/v1/organization/" + orgId + "/users",
|
||||
{
|
||||
@ -16,7 +16,7 @@ const getOrganizationUsers = ({ orgId }) => {
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
if (res?.status == 200) {
|
||||
return (await res.json()).users;
|
||||
} else {
|
||||
console.log("Failed to get org users");
|
@ -2,18 +2,16 @@ import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
/**
|
||||
* This route lets us get the all the orgs of a certain user.
|
||||
* @param {*} req
|
||||
* @param {*} res
|
||||
* @returns
|
||||
*/
|
||||
const getOrganizations = (req, res) => {
|
||||
const getOrganizations = () => {
|
||||
return SecurityClient.fetchCall("/api/v1/organization", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
if (res?.status == 200) {
|
||||
return (await res.json()).organizations;
|
||||
} else {
|
||||
console.log("Failed to get orgs of a user");
|
@ -2,18 +2,15 @@ import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
/**
|
||||
* This route gets the information about a specific user.
|
||||
* @param {*} req
|
||||
* @param {*} res
|
||||
* @returns
|
||||
*/
|
||||
const getUser = (req, res) => {
|
||||
const getUser = () => {
|
||||
return SecurityClient.fetchCall("/api/v1/user", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
if (res?.status == 200) {
|
||||
return (await res.json()).user;
|
||||
} else {
|
||||
console.log("Failed to get user info");
|
@ -1,11 +1,12 @@
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
/**
|
||||
* This route creates a new workspace for a user.
|
||||
* @param {*} workspaceName
|
||||
* This route creates a new workspace for a user within a certain organization.
|
||||
* @param {string} workspaceName - project Name
|
||||
* @param {string} organizationId - org ID
|
||||
* @returns
|
||||
*/
|
||||
const createWorkspace = (workspaceName, organizationId) => {
|
||||
const createWorkspace = ( { workspaceName, organizationId }: { workspaceName: string; organizationId: string; }) => {
|
||||
return SecurityClient.fetchCall("/api/v1/workspace", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
@ -16,7 +17,7 @@ const createWorkspace = (workspaceName, organizationId) => {
|
||||
organizationId: organizationId,
|
||||
}),
|
||||
}).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
if (res?.status == 200) {
|
||||
return (await res.json()).workspace;
|
||||
} else {
|
||||
console.log("Failed to create a project");
|
@ -2,10 +2,10 @@ import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
/**
|
||||
* Get the latest key pairs from a certain workspace
|
||||
* @param {*} workspaceId
|
||||
* @param {string} workspaceId
|
||||
* @returns
|
||||
*/
|
||||
const getLatestFileKey = (workspaceId) => {
|
||||
const getLatestFileKey = ({ workspaceId } : { workspaceId: string; }) => {
|
||||
return SecurityClient.fetchCall(
|
||||
"/api/v1/key/" + workspaceId + "/latest",
|
||||
{
|
||||
@ -15,7 +15,7 @@ const getLatestFileKey = (workspaceId) => {
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
if (res?.status == 200) {
|
||||
return await res.json();
|
||||
} else {
|
||||
console.log("Failed to get the latest key pairs for a certain project");
|
@ -2,13 +2,12 @@ import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
/**
|
||||
* This route lets us get the information of a certain project.
|
||||
* @param {*} req
|
||||
* @param {*} res
|
||||
* @param {*} projectId - project ID (we renamed workspaces to projects in the app)
|
||||
* @returns
|
||||
*/
|
||||
const getWorkspaceInfo = (req, res) => {
|
||||
const getProjectInfo = ({ projectId }: { projectId: string; }) => {
|
||||
return SecurityClient.fetchCall(
|
||||
"/api/v1/workspace/" + req.workspaceId,
|
||||
"/api/v1/workspace/" + projectId,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
@ -16,7 +15,7 @@ const getWorkspaceInfo = (req, res) => {
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
if (res?.status == 200) {
|
||||
return (await res.json()).workspace;
|
||||
} else {
|
||||
console.log("Failed to get project info");
|
||||
@ -24,4 +23,4 @@ const getWorkspaceInfo = (req, res) => {
|
||||
});
|
||||
};
|
||||
|
||||
export default getWorkspaceInfo;
|
||||
export default getProjectInfo;
|
@ -2,13 +2,12 @@ import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
/**
|
||||
* This route lets us get the public keys of everyone in your workspace.
|
||||
* @param {*} req
|
||||
* @param {*} res
|
||||
* @param {string} workspaceId
|
||||
* @returns
|
||||
*/
|
||||
const getWorkspaceKeys = (req, res) => {
|
||||
const getWorkspaceKeys = ({ workspaceId }: { workspaceId: string; }) => {
|
||||
return SecurityClient.fetchCall(
|
||||
"/api/v1/workspace/" + req.workspaceId + "/keys",
|
||||
"/api/v1/workspace/" + workspaceId + "/keys",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
@ -16,7 +15,7 @@ const getWorkspaceKeys = (req, res) => {
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
if (res?.status == 200) {
|
||||
return (await res.json()).publicKeys;
|
||||
} else {
|
||||
console.log("Failed to get the public keys of everyone in the workspace");
|
@ -2,13 +2,12 @@ import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
/**
|
||||
* This route lets us get all the users in the workspace.
|
||||
* @param {*} req
|
||||
* @param {*} res
|
||||
* @param {string} workspaceId - workspace ID
|
||||
* @returns
|
||||
*/
|
||||
const getWorkspaceUsers = (req, res) => {
|
||||
const getWorkspaceUsers = ({ workspaceId }: { workspaceId: string; }) => {
|
||||
return SecurityClient.fetchCall(
|
||||
"/api/v1/workspace/" + req.workspaceId + "/users",
|
||||
"/api/v1/workspace/" + workspaceId + "/users",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
@ -16,7 +15,7 @@ const getWorkspaceUsers = (req, res) => {
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
if (res?.status == 200) {
|
||||
return (await res.json()).users;
|
||||
} else {
|
||||
console.log("Failed to get Project Users");
|
@ -1,24 +0,0 @@
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
/**
|
||||
* This route lets us get the public keys of everyone in your workspace.
|
||||
* @param {*} req
|
||||
* @param {*} res
|
||||
* @returns
|
||||
*/
|
||||
const getWorkspaces = (req, res) => {
|
||||
return SecurityClient.fetchCall("/api/v1/workspace", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).workspaces;
|
||||
} else {
|
||||
console.log("Failed to get projects");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export default getWorkspaces;
|
31
frontend/pages/api/workspace/getWorkspaces.ts
Normal file
31
frontend/pages/api/workspace/getWorkspaces.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
interface Workspaces {
|
||||
__v: number;
|
||||
_id: string;
|
||||
name: string;
|
||||
organization: string;
|
||||
}
|
||||
[];
|
||||
|
||||
/**
|
||||
* This route lets us get the workspaces of a certain user
|
||||
* @returns
|
||||
*/
|
||||
const getWorkspaces = () => {
|
||||
return SecurityClient.fetchCall("/api/v1/workspace", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}).then(async (res) => {
|
||||
if (res?.status == 200) {
|
||||
const data = (await res.json()) as unknown as { workspaces: Workspaces };
|
||||
return data.workspaces;
|
||||
}
|
||||
|
||||
throw new Error("Failed to get projects");
|
||||
});
|
||||
};
|
||||
|
||||
export default getWorkspaces;
|
@ -98,22 +98,22 @@ export default function Home() {
|
||||
|
||||
useEffect(() => {
|
||||
const checkUserActionsFunction = async () => {
|
||||
let userActionSlack = await checkUserAction({
|
||||
const userActionSlack = await checkUserAction({
|
||||
action: "slack_cta_clicked",
|
||||
});
|
||||
setHasUserClickedSlack(userActionSlack ? true : false);
|
||||
|
||||
let userActionIntro = await checkUserAction({
|
||||
const userActionIntro = await checkUserAction({
|
||||
action: "intro_cta_clicked",
|
||||
});
|
||||
setHasUserClickedIntro(userActionIntro ? true : false);
|
||||
|
||||
let userActionStar = await checkUserAction({
|
||||
const userActionStar = await checkUserAction({
|
||||
action: "star_cta_clicked",
|
||||
});
|
||||
setHasUserStarred(userActionStar ? true : false);
|
||||
|
||||
let orgId = localStorage.getItem("orgData.id");
|
||||
const orgId = localStorage.getItem("orgData.id");
|
||||
const orgUsers = await getOrganizationUsers({
|
||||
orgId: orgId ? orgId : "",
|
||||
});
|
||||
@ -123,9 +123,9 @@ export default function Home() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="mx-6 lg:mx-0 w-full overflow-y-scroll pt-28 h-screen">
|
||||
<div className="flex flex-col items-center text-gray-300 text-lg mx-auto max-w-2xl lg:max-w-3xl xl:max-w-4xl">
|
||||
<div className="text-3xl font-bold text-left w-full pt-6">Your quick start guide</div>
|
||||
<div className="mx-6 lg:mx-0 w-full overflow-y-scroll pt-20 h-screen">
|
||||
<div className="flex flex-col items-center text-gray-300 text-lg mx-auto max-w-2xl lg:max-w-3xl xl:max-w-4xl py-6">
|
||||
<div className="text-3xl font-bold text-left w-full">Your quick start guide</div>
|
||||
<div className="text-md text-left w-full pt-2 pb-4 text-bunker-300">Click on the items below and follow the instructions.</div>
|
||||
{learningItem({ text: "Get to know Infisical", subText: "", complete: hasUserClickedIntro, icon: faHandPeace, time: "3 min", userAction: "intro_cta_clicked", link: "https://www.youtube.com/watch?v=JS3OKYU2078" })}
|
||||
{learningItem({ text: "Add your secrets", subText: "Click to see example secrets, and add your own.", complete: false, icon: faPlus, time: "2 min", userAction: "first_time_secrets_pushed", link: "/dashboard/" + router.query.id })}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import ReactCodeInput from "react-code-input";
|
||||
import dynamic from "next/dynamic";
|
||||
import Head from "next/head";
|
||||
import Image from "next/image";
|
||||
@ -20,7 +21,7 @@ import completeAccountInformationSignup from "./api/auth/CompleteAccountInformat
|
||||
import sendVerificationEmail from "./api/auth/SendVerificationEmail";
|
||||
import getWorkspaces from "./api/workspace/getWorkspaces";
|
||||
|
||||
const ReactCodeInput = dynamic(import("react-code-input"));
|
||||
// const ReactCodeInput = dynamic(import("react-code-input"));
|
||||
const nacl = require("tweetnacl");
|
||||
const jsrp = require("jsrp");
|
||||
nacl.util = require("tweetnacl-util");
|
||||
@ -42,7 +43,7 @@ const props = {
|
||||
border: "1px solid gray",
|
||||
textAlign: "center",
|
||||
},
|
||||
};
|
||||
} as const;
|
||||
const propsPhone = {
|
||||
inputStyle: {
|
||||
fontFamily: "monospace",
|
||||
@ -58,7 +59,7 @@ const propsPhone = {
|
||||
border: "1px solid gray",
|
||||
textAlign: "center",
|
||||
},
|
||||
};
|
||||
} as const;
|
||||
|
||||
export default function SignUp() {
|
||||
const [email, setEmail] = useState("");
|
||||
@ -85,15 +86,16 @@ export default function SignUp() {
|
||||
const [verificationToken, setVerificationToken] = useState();
|
||||
const [backupKeyIssued, setBackupKeyIssued] = useState(false);
|
||||
|
||||
useEffect(async () => {
|
||||
let userWorkspace;
|
||||
try {
|
||||
const userWorkspaces = await getWorkspaces();
|
||||
userWorkspace = userWorkspaces[0]._id;
|
||||
router.push("/dashboard/" + userWorkspace);
|
||||
} catch (error) {
|
||||
console.log("Error - Not logged in yet");
|
||||
}
|
||||
useEffect(() => {
|
||||
const tryAuth = async () => {
|
||||
try {
|
||||
const userWorkspaces = await getWorkspaces();
|
||||
router.push("/dashboard/" + userWorkspaces[0]._id);
|
||||
} catch (error) {
|
||||
console.log("Error - Not logged in yet");
|
||||
}
|
||||
};
|
||||
tryAuth();
|
||||
}, []);
|
||||
|
||||
/**
|
||||
@ -108,7 +110,7 @@ export default function SignUp() {
|
||||
} else if (step == 2) {
|
||||
// Checking if the code matches the email.
|
||||
const response = await checkEmailVerificationCode(email, code);
|
||||
if (response.status == "200" || code == "111222") {
|
||||
if (response.status === 200 || code == "111222") {
|
||||
setVerificationToken((await response.json()).token);
|
||||
setStep(3);
|
||||
} else {
|
||||
@ -123,7 +125,7 @@ export default function SignUp() {
|
||||
* Verifies if the entered email "looks" correct
|
||||
*/
|
||||
const emailCheck = () => {
|
||||
var emailCheckBool = false;
|
||||
let emailCheckBool = false;
|
||||
if (!email) {
|
||||
setEmailError(true);
|
||||
setEmailErrorMessage("Please enter your email.");
|
||||
@ -150,7 +152,7 @@ export default function SignUp() {
|
||||
// Verifies if the imformation that the users entered (name, workspace) is there, and if the password matched the criteria.
|
||||
const signupErrorCheck = async () => {
|
||||
setIsLoading(true);
|
||||
var errorCheck = false;
|
||||
let errorCheck = false;
|
||||
if (!firstName) {
|
||||
setFirstNameError(true);
|
||||
errorCheck = true;
|
||||
@ -163,13 +165,13 @@ export default function SignUp() {
|
||||
} else {
|
||||
setLastNameError(false);
|
||||
}
|
||||
errorCheck = passwordCheck(
|
||||
errorCheck = passwordCheck({
|
||||
password,
|
||||
setPasswordErrorLength,
|
||||
setPasswordErrorNumber,
|
||||
setPasswordErrorLowerCase,
|
||||
errorCheck
|
||||
);
|
||||
currentErrorCheck: errorCheck,
|
||||
});
|
||||
|
||||
if (!errorCheck) {
|
||||
// Generate a random pair of a public and a private key
|
||||
@ -187,7 +189,8 @@ export default function SignUp() {
|
||||
32 + (password.slice(0, 32).length - new Blob([password]).size),
|
||||
"0"
|
||||
)
|
||||
);
|
||||
) as { ciphertext: string; iv: string; tag: string };
|
||||
|
||||
localStorage.setItem("PRIVATE_KEY", PRIVATE_KEY);
|
||||
|
||||
client.init(
|
||||
@ -196,45 +199,47 @@ export default function SignUp() {
|
||||
password: password,
|
||||
},
|
||||
async () => {
|
||||
client.createVerifier(async (err, result) => {
|
||||
const response = await completeAccountInformationSignup({
|
||||
email,
|
||||
firstName,
|
||||
lastName,
|
||||
organizationName: firstName + "'s organization",
|
||||
publicKey: PUBLIC_KEY,
|
||||
ciphertext,
|
||||
iv,
|
||||
tag,
|
||||
salt: result.salt,
|
||||
verifier: result.verifier,
|
||||
token: verificationToken,
|
||||
});
|
||||
client.createVerifier(
|
||||
async (err: any, result: { salt: string; verifier: string }) => {
|
||||
const response = await completeAccountInformationSignup({
|
||||
email,
|
||||
firstName,
|
||||
lastName,
|
||||
organizationName: firstName + "'s organization",
|
||||
publicKey: PUBLIC_KEY,
|
||||
ciphertext,
|
||||
iv,
|
||||
tag,
|
||||
salt: result.salt,
|
||||
verifier: result.verifier,
|
||||
token: verificationToken,
|
||||
});
|
||||
|
||||
// if everything works, go the main dashboard page.
|
||||
if (!errorCheck && response.status == "200") {
|
||||
response = await response.json();
|
||||
// if everything works, go the main dashboard page.
|
||||
if (response.status === 200) {
|
||||
// response = await response.json();
|
||||
|
||||
localStorage.setItem("publicKey", PUBLIC_KEY);
|
||||
localStorage.setItem("encryptedPrivateKey", ciphertext);
|
||||
localStorage.setItem("iv", iv);
|
||||
localStorage.setItem("tag", tag);
|
||||
localStorage.setItem("publicKey", PUBLIC_KEY);
|
||||
localStorage.setItem("encryptedPrivateKey", ciphertext);
|
||||
localStorage.setItem("iv", iv);
|
||||
localStorage.setItem("tag", tag);
|
||||
|
||||
try {
|
||||
await attemptLogin(
|
||||
email,
|
||||
password,
|
||||
setErrorLogin,
|
||||
router,
|
||||
true,
|
||||
false
|
||||
);
|
||||
incrementStep();
|
||||
} catch (error) {
|
||||
setIsLoading(false);
|
||||
try {
|
||||
await attemptLogin(
|
||||
email,
|
||||
password,
|
||||
setErrorLogin,
|
||||
router,
|
||||
true,
|
||||
false
|
||||
);
|
||||
incrementStep();
|
||||
} catch (error) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
@ -296,6 +301,8 @@ export default function SignUp() {
|
||||
</p>
|
||||
<div className="hidden md:block">
|
||||
<ReactCodeInput
|
||||
name=""
|
||||
inputMode="tel"
|
||||
type="text"
|
||||
fields={6}
|
||||
onChange={setCode}
|
||||
@ -305,6 +312,8 @@ export default function SignUp() {
|
||||
</div>
|
||||
<div className="block md:hidden">
|
||||
<ReactCodeInput
|
||||
name=""
|
||||
inputMode="tel"
|
||||
type="text"
|
||||
fields={6}
|
||||
onChange={setCode}
|
||||
@ -364,15 +373,15 @@ export default function SignUp() {
|
||||
<div className="mt-2 flex flex-col items-center justify-center w-full md:p-2 rounded-lg max-h-60">
|
||||
<InputField
|
||||
label="Password"
|
||||
onChangeHandler={(password) => {
|
||||
onChangeHandler={(password: string) => {
|
||||
setPassword(password);
|
||||
passwordCheck(
|
||||
passwordCheck({
|
||||
password,
|
||||
setPasswordErrorLength,
|
||||
setPasswordErrorNumber,
|
||||
setPasswordErrorLowerCase,
|
||||
false
|
||||
);
|
||||
currentErrorCheck: false,
|
||||
});
|
||||
}}
|
||||
type="password"
|
||||
value={password}
|
||||
@ -496,7 +505,7 @@ export default function SignUp() {
|
||||
setBackupKeyIssued,
|
||||
});
|
||||
const userWorkspaces = await getWorkspaces();
|
||||
let userWorkspace = userWorkspaces[0]._id;
|
||||
const userWorkspace = userWorkspaces[0]._id;
|
||||
router.push("/home/" + userWorkspace);
|
||||
}}
|
||||
size="lg"
|
@ -92,7 +92,7 @@ export default function SignupInvite() {
|
||||
},
|
||||
async () => {
|
||||
client.createVerifier(async (err, result) => {
|
||||
const response = await completeAccountInformationSignupInvite({
|
||||
let response = await completeAccountInformationSignupInvite({
|
||||
email,
|
||||
firstName,
|
||||
lastName,
|
||||
@ -291,7 +291,7 @@ export default function SignupInvite() {
|
||||
<div className="py-2"></div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col items-center justify-center w-full md:px-4 md:py-5 mt-2 px-2 py-3 max-h-24 max-w-md mx-auto text-lg">
|
||||
<div className="flex flex-col items-center justify-center md:px-4 md:py-5 mt-2 px-2 py-3 max-h-24 max-w-max mx-auto text-lg">
|
||||
<Button
|
||||
text="Sign Up"
|
||||
onButtonPressed={() => {
|
||||
|
16
helm-charts/README.md
Normal file
16
helm-charts/README.md
Normal file
@ -0,0 +1,16 @@
|
||||
### helm repository Setup
|
||||
Assuming you have helm already installed, it is straight-forward to add a Cloudsmith-based chart repository:
|
||||
|
||||
```
|
||||
helm repo add infisical-helm-charts 'https://dl.cloudsmith.io/public/infisical/helm-charts/helm/charts/'
|
||||
|
||||
helm repo update
|
||||
```
|
||||
|
||||
### Installing a Helm Chart
|
||||
```
|
||||
helm install infisical-helm-charts/<name-of-helm-chart>
|
||||
```
|
||||
|
||||
#### Available chart names
|
||||
- infisical
|
@ -4,7 +4,6 @@ metadata:
|
||||
name: {{ .Release.Name }}-backend-deployment
|
||||
labels:
|
||||
app: backend
|
||||
namespace: {{ .Values.namespace }}
|
||||
spec:
|
||||
replicas: {{ .Values.backend.replicaCount }}
|
||||
selector:
|
||||
@ -23,6 +22,9 @@ spec:
|
||||
- containerPort: 4000
|
||||
env:
|
||||
{{- range $key, $value := .Values.secrets }}
|
||||
{{- if eq $value "MUST_REPLACE" }}
|
||||
{{ fail "Environment variables are not set. Please set all environment variables to continue." }}
|
||||
{{ end }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $value }}
|
||||
{{- end }}
|
||||
@ -32,7 +34,6 @@ apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: infisical-backend-service
|
||||
namespace: {{ .Values.namespace }}
|
||||
spec:
|
||||
selector:
|
||||
app: backend
|
||||
|
@ -4,7 +4,6 @@ metadata:
|
||||
name: {{ .Release.Name }}-frontend-deployment
|
||||
labels:
|
||||
app: frontend
|
||||
namespace: {{ .Values.namespace }}
|
||||
spec:
|
||||
replicas: {{ .Values.frontend.replicaCount }}
|
||||
selector:
|
||||
@ -26,7 +25,6 @@ apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: infisical-frontend-service
|
||||
namespace: {{ .Values.namespace }}
|
||||
spec:
|
||||
selector:
|
||||
app: frontend
|
||||
|
@ -1,8 +1,8 @@
|
||||
{{ if .Values.ingress.enabled }}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: infisical-ingress
|
||||
namespace: {{ .Values.namespace }}
|
||||
{{- with .Values.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
@ -22,17 +22,18 @@ spec:
|
||||
- host: {{ .Values.ingress.hostName}}
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
- path: {{ .Values.ingress.frontend.path }}
|
||||
pathType: {{ .Values.ingress.frontend.pathType }}
|
||||
backend:
|
||||
service:
|
||||
name: infisical-frontend-service
|
||||
port:
|
||||
number: 3000
|
||||
- path: /api
|
||||
pathType: Prefix
|
||||
- path: {{ .Values.ingress.backend.path }}
|
||||
pathType: {{ .Values.ingress.backend.pathType }}
|
||||
backend:
|
||||
service:
|
||||
name: infisical-backend-service
|
||||
port:
|
||||
number: 4000
|
||||
number: 4000
|
||||
{{ end }}
|
38
helm-charts/infisical/templates/mongodb-deployment.yaml
Normal file
38
helm-charts/infisical/templates/mongodb-deployment.yaml
Normal file
@ -0,0 +1,38 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mongodb-deployment
|
||||
labels:
|
||||
app: mongodb
|
||||
spec:
|
||||
replicas: 1 # Cannot be scaled. To scale, you must set up Stateful Set
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mongodb
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mongodb
|
||||
spec:
|
||||
containers:
|
||||
- name: mongodb
|
||||
image: mongo
|
||||
ports:
|
||||
- containerPort: 27017
|
||||
env:
|
||||
- name: MONGO_INITDB_ROOT_USERNAME
|
||||
value: root
|
||||
- name: MONGO_INITDB_ROOT_PASSWORD
|
||||
value: root
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mongodb-service
|
||||
spec:
|
||||
selector:
|
||||
app: mongodb
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 27017
|
||||
targetPort: 27017 # container port
|
@ -1,4 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: infisical
|
@ -1,3 +1,8 @@
|
||||
#####
|
||||
# INFISICAL K8 DEFAULT VALUES FIL
|
||||
# PLEASE REPLACE VALUES/EDIT AS REQUIRED
|
||||
#####
|
||||
|
||||
namespace: infisical
|
||||
|
||||
frontend:
|
||||
@ -15,22 +20,57 @@ backend:
|
||||
tag: "latest"
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
annotations: []
|
||||
enabled: true
|
||||
annotations: {}
|
||||
hostName: example.com
|
||||
tls: {}
|
||||
frontend:
|
||||
path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
path: /api
|
||||
pathType: Prefix
|
||||
tls: []
|
||||
|
||||
## Complete Ingress example
|
||||
# ingress:
|
||||
# enabled: true
|
||||
# annotations:
|
||||
# kubernetes.io/ingress.class: "nginx"
|
||||
# cert-manager.io/issuer: letsencrypt-nginx
|
||||
# hostName: example.com
|
||||
# frontend:
|
||||
# path: /
|
||||
# pathType: Prefix
|
||||
# backend:
|
||||
# path: /api
|
||||
# pathType: Prefix
|
||||
# tls:
|
||||
# hosts:
|
||||
# - k8.infisical.com
|
||||
# secretName: letsencrypt-nginx
|
||||
|
||||
###
|
||||
### YOU MUST FILL IN ALL SECRETS BELOW
|
||||
###
|
||||
secrets:
|
||||
PRIVATE_KEY: REQUIRED
|
||||
PUBLIC_KEY: REQUIRED
|
||||
ENCRYPTION_KEY: REQUIRED
|
||||
JWT_SIGNUP_SECRET: REQUIRED
|
||||
JWT_REFRESH_SECRET: REQUIRED
|
||||
JWT_AUTH_SECRET: REQUIRED
|
||||
NODE_ENV: development
|
||||
SMTP_HOST: REQUIRED
|
||||
SMTP_NAME: REQUIRED
|
||||
SMTP_USERNAME: REQUIRED
|
||||
SMTP_PASSWORD: REQUIRED
|
||||
MONGO_URL: REQUIRED
|
||||
# Required keys for platform encryption/decryption ops. Replace with nacl sk keys
|
||||
PRIVATE_KEY: MUST_REPLACE
|
||||
PUBLIC_KEY: MUST_REPLACE
|
||||
ENCRYPTION_KEY: MUST_REPLACE
|
||||
|
||||
# JWT
|
||||
# Required secrets to sign JWT tokens
|
||||
JWT_SIGNUP_SECRET: MUST_REPLACE
|
||||
JWT_REFRESH_SECRET: MUST_REPLACE
|
||||
JWT_AUTH_SECRET: MUST_REPLACE
|
||||
|
||||
# Mail/SMTP
|
||||
# Required to send emails
|
||||
SMTP_HOST: MUST_REPLACE
|
||||
SMTP_NAME: MUST_REPLACE
|
||||
SMTP_USERNAME: MUST_REPLACE
|
||||
SMTP_PASSWORD: MUST_REPLACE
|
||||
|
||||
# You may replace with Mongo Cloud URI
|
||||
MONGO_URL: mongodb://root:root@mongodb-service:27017/
|
||||
|
10
helm-charts/upload-to-cloudsmith.sh
Normal file
10
helm-charts/upload-to-cloudsmith.sh
Normal file
@ -0,0 +1,10 @@
|
||||
## Loop through each helm chart directoy and build each into helm package
|
||||
for d in */ ; do
|
||||
helm package $d
|
||||
done
|
||||
|
||||
## Upload each packaged helm chart
|
||||
for i in *.tgz; do
|
||||
[ -f "$i" ] || break
|
||||
cloudsmith push helm --republish infisical/helm-charts $i
|
||||
done
|
Reference in New Issue
Block a user