mirror of
https://github.com/Infisical/infisical.git
synced 2026-01-10 07:58:15 -05:00
Merge branch 'main' of https://github.com/Infisical/infisical
This commit is contained in:
46
frontend/.eslintrc.json
Normal file
46
frontend/.eslintrc.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals",
|
||||
"plugins": [
|
||||
"simple-import-sort"
|
||||
],
|
||||
"rules": {
|
||||
"react-hooks/exhaustive-deps": "off",
|
||||
"simple-import-sort/exports": "warn",
|
||||
"simple-import-sort/imports": [
|
||||
"warn",
|
||||
{
|
||||
"groups": [
|
||||
// Node.js builtins. You could also generate this regex if you use a `.js` config.
|
||||
// For example: `^(${require("module").builtinModules.join("|")})(/|$)`
|
||||
// Note that if you use the `node:` prefix for Node.js builtins,
|
||||
// you can avoid this complexity: You can simply use "^node:".
|
||||
[
|
||||
"^(assert|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|https|module|net|os|path|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|tty|url|util|vm|zlib|freelist|v8|process|async_hooks|http2|perf_hooks)(/.*|$)"
|
||||
],
|
||||
// Packages `react` related packages
|
||||
[
|
||||
"^react",
|
||||
"^next",
|
||||
"^@?\\w"
|
||||
],
|
||||
// Internal packages.
|
||||
[
|
||||
"^~(/.*|$)"
|
||||
],
|
||||
// Relative imports
|
||||
[
|
||||
"^\\.\\.(?!/?$)",
|
||||
"^\\.\\./?$",
|
||||
"^\\./(?=.*/)(?!/?$)",
|
||||
"^\\.(?!/?$)",
|
||||
"^\\./?$"
|
||||
],
|
||||
// Style imports.
|
||||
[
|
||||
"^.+\\.?(css|scss)$"
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import checkAuth from "../pages/api/auth/CheckAuth";
|
||||
import { useEffect,useState } from "react";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import checkAuth from "~/pages/api/auth/CheckAuth";
|
||||
|
||||
import { publicPaths } from "../const";
|
||||
|
||||
// #TODO: finish spinner only when the data loads fully
|
||||
@@ -11,9 +13,11 @@ export default function RouteGuard({ children }) {
|
||||
const router = useRouter();
|
||||
const [authorized, setAuthorized] = useState(false);
|
||||
|
||||
useEffect(async () => {
|
||||
useEffect(() => {
|
||||
// on initial load - run auth check
|
||||
await authCheck(router.asPath);
|
||||
(async () => {
|
||||
await authCheck(router.asPath);
|
||||
})();
|
||||
|
||||
// on route change start - hide page content by setting authorized to false
|
||||
// #TODO: add the loading page when not yet authorized.
|
||||
@@ -31,7 +35,6 @@ export default function RouteGuard({ children }) {
|
||||
router.events.off("routeChangeComplete", authCheck);
|
||||
// router.events.off("routeChangeError", onError);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
@@ -79,4 +82,4 @@ export default function RouteGuard({ children }) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import posthog from "posthog-js";
|
||||
|
||||
import { ENV, POSTHOG_API_KEY, POSTHOG_HOST, TELEMETRY_ENABLED } from "../utilities/config";
|
||||
|
||||
export const initPostHog = () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faExclamationTriangle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
export default function Error({ text }) {
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import Error from "./Error";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import {
|
||||
faCircle,
|
||||
faCircleExclamation,
|
||||
@@ -8,9 +8,10 @@ import {
|
||||
faEye,
|
||||
faEyeSlash,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import guidGenerator from "../utilities/randomId";
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import Error from "./Error";
|
||||
|
||||
const InputField = (props) => {
|
||||
const [passwordVisible, setPasswordVisible] = useState(false);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from "react";
|
||||
import { Listbox, Transition } from "@headlessui/react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faCheck, faPlus, faAngleDown } from "@fortawesome/free-solid-svg-icons";
|
||||
import { Fragment } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { faAngleDown,faCheck, faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Listbox, Transition } from "@headlessui/react";
|
||||
|
||||
/**
|
||||
* This is the component that we use for drop down lists.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import Link from "next/link";
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
var classNames = require("classnames");
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { Fragment, useState } from "react";
|
||||
import InputField from "../InputField";
|
||||
import addIncidentContact from "../../../pages/api/organization/addIncidentContact";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
|
||||
import addIncidentContact from "~/pages/api/organization/addIncidentContact";
|
||||
|
||||
import Button from "../buttons/Button";
|
||||
import InputField from "../InputField";
|
||||
|
||||
const AddIncidentContactDialog = ({
|
||||
isOpen,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { Fragment, useState } from "react";
|
||||
import ListBox from "../Listbox";
|
||||
import { useRouter } from "next/router";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
|
||||
import Button from "../buttons/Button";
|
||||
import ListBox from "../Listbox";
|
||||
|
||||
const AddProjectMemberDialog = ({
|
||||
isOpen,
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { Fragment, useState } from "react";
|
||||
import ListBox from "../Listbox";
|
||||
import { useRouter } from "next/router";
|
||||
import Button from "../buttons/Button";
|
||||
import InputField from "../InputField";
|
||||
import getLatestFileKey from "../../../pages/api/workspace/getLatestFileKey";
|
||||
import { decryptAssymmetric, encryptAssymmetric } from "../../utilities/crypto";
|
||||
import addServiceToken from "../../../pages/api/serviceToken/addServiceToken";
|
||||
import nacl from "tweetnacl";
|
||||
import { faCheck, faCopy } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import nacl from "tweetnacl";
|
||||
|
||||
import addServiceToken from "~/pages/api/serviceToken/addServiceToken";
|
||||
import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey";
|
||||
|
||||
import { decryptAssymmetric, encryptAssymmetric } from "../../utilities/crypto";
|
||||
import Button from "../buttons/Button";
|
||||
import InputField from "../InputField";
|
||||
import ListBox from "../Listbox";
|
||||
|
||||
const envMapping = {
|
||||
Development: "dev",
|
||||
@@ -28,7 +30,7 @@ const AddServiceTokenDialog = ({
|
||||
isOpen,
|
||||
closeModal,
|
||||
workspaceId,
|
||||
workspaceName
|
||||
workspaceName,
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const [serviceToken, setServiceToken] = useState("");
|
||||
@@ -38,52 +40,52 @@ 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,
|
||||
nonce: latestFileKey.latestKey.nonce,
|
||||
publicKey: latestFileKey.latestKey.sender.publicKey,
|
||||
privateKey: localStorage.getItem("PRIVATE_KEY")
|
||||
privateKey: localStorage.getItem("PRIVATE_KEY"),
|
||||
});
|
||||
|
||||
// generate new public/private key pair
|
||||
const pair = nacl.box.keyPair();
|
||||
const publicKey = nacl.util.encodeBase64(pair.publicKey);
|
||||
const privateKey = nacl.util.encodeBase64(pair.secretKey);
|
||||
|
||||
|
||||
// encrypt workspace key under newly-generated public key
|
||||
const { ciphertext: encryptedKey, nonce } = encryptAssymmetric({
|
||||
plaintext: key,
|
||||
publicKey,
|
||||
privateKey
|
||||
privateKey,
|
||||
});
|
||||
|
||||
let newServiceToken = await addServiceToken({
|
||||
name: serviceTokenName,
|
||||
workspaceId,
|
||||
name: serviceTokenName,
|
||||
workspaceId,
|
||||
environment: envMapping[serviceTokenEnv],
|
||||
expiresIn: expiryMapping[serviceTokenExpiresIn],
|
||||
publicKey,
|
||||
expiresIn: expiryMapping[serviceTokenExpiresIn],
|
||||
publicKey,
|
||||
encryptedKey,
|
||||
nonce
|
||||
})
|
||||
|
||||
const serviceToken = newServiceToken + ',' + privateKey;
|
||||
nonce,
|
||||
});
|
||||
|
||||
const serviceToken = newServiceToken + "," + privateKey;
|
||||
setServiceToken(serviceToken);
|
||||
}
|
||||
};
|
||||
|
||||
function copyToClipboard() {
|
||||
// Get the text field
|
||||
var copyText = document.getElementById("serviceToken");
|
||||
|
||||
|
||||
// Select the text field
|
||||
copyText.select();
|
||||
copyText.setSelectionRange(0, 99999); // For mobile devices
|
||||
|
||||
// Copy the text inside the text field
|
||||
|
||||
// Copy the text inside the text field
|
||||
navigator.clipboard.writeText(copyText.value);
|
||||
|
||||
|
||||
setServiceTokenCopied(true);
|
||||
setTimeout(() => setServiceTokenCopied(false), 2000);
|
||||
// Alert the copied text
|
||||
@@ -94,7 +96,7 @@ const AddServiceTokenDialog = ({
|
||||
closeModal();
|
||||
setServiceTokenName("");
|
||||
setServiceToken("");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="z-50">
|
||||
@@ -123,18 +125,25 @@ const AddServiceTokenDialog = ({
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
{serviceToken == ""
|
||||
? <Dialog.Panel className="w-full max-w-md transform rounded-md bg-bunker-800 border border-gray-700 p-6 text-left align-middle shadow-xl transition-all">
|
||||
{serviceToken == "" ? (
|
||||
<Dialog.Panel className="w-full max-w-md transform rounded-md bg-bunker-800 border border-gray-700 p-6 text-left align-middle shadow-xl transition-all">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-medium leading-6 text-gray-400 z-50"
|
||||
>
|
||||
Add a service token for {workspaceName}
|
||||
Add a service token for{" "}
|
||||
{workspaceName}
|
||||
</Dialog.Title>
|
||||
<div className="mt-2 mb-4">
|
||||
<div className="flex flex-col">
|
||||
<p className="text-sm text-gray-500">
|
||||
Specify the name, environment, and expiry period. When a token is generated, you will only be able to see it once before it disappears. Make sure to save it somewhere.
|
||||
Specify the name,
|
||||
environment, and expiry
|
||||
period. When a token is
|
||||
generated, you will only be
|
||||
able to see it once before
|
||||
it disappears. Make sure to
|
||||
save it somewhere.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -154,7 +163,12 @@ const AddServiceTokenDialog = ({
|
||||
<ListBox
|
||||
selected={serviceTokenEnv}
|
||||
onChange={setServiceTokenEnv}
|
||||
data={["Development", "Staging", "Production", "Testing"]}
|
||||
data={[
|
||||
"Development",
|
||||
"Staging",
|
||||
"Production",
|
||||
"Testing",
|
||||
]}
|
||||
width="full"
|
||||
text="Environment: "
|
||||
/>
|
||||
@@ -162,8 +176,14 @@ const AddServiceTokenDialog = ({
|
||||
<div className="max-h-28">
|
||||
<ListBox
|
||||
selected={serviceTokenExpiresIn}
|
||||
onChange={setServiceTokenExpiresIn}
|
||||
data={["1 day", "7 days", "1 month"]}
|
||||
onChange={
|
||||
setServiceTokenExpiresIn
|
||||
}
|
||||
data={[
|
||||
"1 day",
|
||||
"7 days",
|
||||
"1 month",
|
||||
]}
|
||||
width="full"
|
||||
text="Expires in: "
|
||||
/>
|
||||
@@ -171,17 +191,24 @@ const AddServiceTokenDialog = ({
|
||||
<div className="max-w-max">
|
||||
<div className="mt-6 flex flex-col justify-start w-max">
|
||||
<Button
|
||||
onButtonPressed={() => generateServiceToken()}
|
||||
onButtonPressed={() =>
|
||||
generateServiceToken()
|
||||
}
|
||||
color="mineshaft"
|
||||
text="Add Service Token"
|
||||
textDisabled="Add Service Token"
|
||||
size="md"
|
||||
active={serviceTokenName == "" ? false : true}
|
||||
active={
|
||||
serviceTokenName == ""
|
||||
? false
|
||||
: true
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
: <Dialog.Panel className="w-full max-w-md transform rounded-md bg-bunker-800 border border-gray-700 p-6 text-left align-middle shadow-xl transition-all">
|
||||
) : (
|
||||
<Dialog.Panel className="w-full max-w-md transform rounded-md bg-bunker-800 border border-gray-700 p-6 text-left align-middle shadow-xl transition-all">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-medium leading-6 text-gray-400 z-50"
|
||||
@@ -191,17 +218,40 @@ const AddServiceTokenDialog = ({
|
||||
<div className="mt-2 mb-4">
|
||||
<div className="flex flex-col">
|
||||
<p className="text-sm text-gray-500">
|
||||
Once you close this popup, you will never see your service token again
|
||||
Once you close this popup,
|
||||
you will never see your
|
||||
service token again
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<div className="flex justify-end items-center bg-white/[0.07] text-base mt-2 mr-2 rounded-md text-gray-400 w-full h-36">
|
||||
<input type="text" value={serviceToken} id="serviceToken" className="invisible bg-white/0 text-gray-400 py-2 w-full px-2 min-w-full outline-none"></input>
|
||||
<div className="bg-white/0 max-w-md text-sm text-gray-400 py-2 w-full pl-14 pr-2 break-words outline-none">{serviceToken}</div>
|
||||
<input
|
||||
type="text"
|
||||
value={serviceToken}
|
||||
id="serviceToken"
|
||||
className="invisible bg-white/0 text-gray-400 py-2 w-full px-2 min-w-full outline-none"
|
||||
></input>
|
||||
<div className="bg-white/0 max-w-md text-sm text-gray-400 py-2 w-full pl-14 pr-2 break-words outline-none">
|
||||
{serviceToken}
|
||||
</div>
|
||||
<div className="group font-normal h-full relative inline-block text-gray-400 underline hover:text-primary duration-200">
|
||||
<button onClick={copyToClipboard} className="h-full pl-3.5 pr-4 border-l border-white/20 py-2 hover:bg-white/[0.12] duration-200">
|
||||
{serviceTokenCopied ? <FontAwesomeIcon icon={faCheck} className="pr-0.5"/> : <FontAwesomeIcon icon={faCopy} />}
|
||||
<button
|
||||
onClick={
|
||||
copyToClipboard
|
||||
}
|
||||
className="h-full pl-3.5 pr-4 border-l border-white/20 py-2 hover:bg-white/[0.12] duration-200"
|
||||
>
|
||||
{serviceTokenCopied ? (
|
||||
<FontAwesomeIcon
|
||||
icon={faCheck}
|
||||
className="pr-0.5"
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon
|
||||
icon={faCopy}
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
<span className="absolute hidden group-hover:flex group-hover:animate-popup duration-300 w-28 -left-8 -top-20 translate-y-full px-3 py-2 bg-chicago-900 rounded-md text-center text-gray-400 text-sm">
|
||||
Click to Copy
|
||||
@@ -211,14 +261,16 @@ const AddServiceTokenDialog = ({
|
||||
</div>
|
||||
<div className="mt-6 flex flex-col justify-start w-max">
|
||||
<Button
|
||||
onButtonPressed={() => closeAddServiceTokenModal()}
|
||||
onButtonPressed={() =>
|
||||
closeAddServiceTokenModal()
|
||||
}
|
||||
color="mineshaft"
|
||||
text="Close"
|
||||
size="md"
|
||||
/>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
}
|
||||
)}
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { Fragment } from "react";
|
||||
import InputField from "../InputField";
|
||||
import { useRouter } from "next/router";
|
||||
import Button from "../buttons/Button";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
|
||||
import { STRIPE_PRODUCT_STARTER } from "../../utilities/config";
|
||||
import Button from "../buttons/Button";
|
||||
import InputField from "../InputField";
|
||||
|
||||
const AddUserDialog = ({
|
||||
isOpen,
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { Fragment, useState } from "react";
|
||||
import InputField from "../InputField";
|
||||
import Image from "next/image";
|
||||
import { Checkbox } from "../table/Checkbox";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
|
||||
import Button from "../buttons/Button";
|
||||
import InputField from "../InputField";
|
||||
import { Checkbox } from "../table/Checkbox";
|
||||
|
||||
/**
|
||||
* The dialog modal for when the user wants to create a new workspace
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
import { Fragment, useState } from 'react'
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
|
||||
import InputField from '../InputField';
|
||||
|
||||
// #TODO: USE THIS. Currently it's not. Kinda complicated to set up because of state.
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { useEffect, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
faGear,
|
||||
faHouse,
|
||||
faLink,
|
||||
faMobile,
|
||||
faUser,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import getOrganizations from "~/pages/api/organization/getOrgs";
|
||||
import getOrganizationUserProjects from "~/pages/api/organization/GetOrgUserProjects";
|
||||
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 NavBarDashboard from "../navigation/NavBarDashboard";
|
||||
import Listbox from "./Listbox";
|
||||
import getWorkspaces from "../../pages/api/workspace/getWorkspaces";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faHouse,
|
||||
faUser,
|
||||
faGear,
|
||||
faMobile,
|
||||
faLink,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import AddWorkspaceDialog from "./dialog/AddWorkspaceDialog";
|
||||
import createWorkspace from "../../pages/api/workspace/createWorkspace";
|
||||
import getOrganizationUserProjects from "../../pages/api/organization/GetOrgUserProjects";
|
||||
import getOrganizationUsers from "../../pages/api/organization/GetOrgUsers";
|
||||
import addUserToWorkspace from "../../pages/api/workspace/addUserToWorkspace";
|
||||
import getOrganizations from "../../pages/api/organization/getOrgs";
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
import { decryptAssymmetric, encryptAssymmetric } from "../utilities/crypto";
|
||||
import Button from "./buttons/Button";
|
||||
import AddWorkspaceDialog from "./dialog/AddWorkspaceDialog";
|
||||
import Listbox from "./Listbox";
|
||||
|
||||
export default function Layout({ children }) {
|
||||
const router = useRouter();
|
||||
@@ -48,9 +49,7 @@ export default function Layout({ children }) {
|
||||
setLoading(true);
|
||||
setTimeout(() => setLoading(false), 1500);
|
||||
const workspaces = await getWorkspaces();
|
||||
const currentWorkspaces = workspaces.map(
|
||||
(workspace) => workspace.name
|
||||
);
|
||||
const currentWorkspaces = workspaces.map((workspace) => workspace.name);
|
||||
if (!currentWorkspaces.includes(workspaceName)) {
|
||||
const newWorkspace = await createWorkspace(
|
||||
workspaceName,
|
||||
@@ -141,7 +140,10 @@ export default function Layout({ children }) {
|
||||
|
||||
useEffect(async () => {
|
||||
// Put a user in a workspace if they're not in one yet
|
||||
if (localStorage.getItem("orgData.id") == null || localStorage.getItem("orgData.id") == "") {
|
||||
if (
|
||||
localStorage.getItem("orgData.id") == null ||
|
||||
localStorage.getItem("orgData.id") == ""
|
||||
) {
|
||||
const userOrgs = await getOrganizations();
|
||||
localStorage.setItem("orgData.id", userOrgs[0]._id);
|
||||
}
|
||||
@@ -150,7 +152,11 @@ export default function Layout({ children }) {
|
||||
orgId: localStorage.getItem("orgData.id"),
|
||||
});
|
||||
let userWorkspaces = orgUserProjects;
|
||||
if (userWorkspaces.length == 0 && (router.asPath != "/noprojects" && !router.asPath.includes("settings"))) {
|
||||
if (
|
||||
userWorkspaces.length == 0 &&
|
||||
router.asPath != "/noprojects" &&
|
||||
!router.asPath.includes("settings")
|
||||
) {
|
||||
router.push("/noprojects");
|
||||
} else if (router.asPath != "/noprojects") {
|
||||
const intendedWorkspaceId = router.asPath
|
||||
@@ -158,7 +164,9 @@ export default function Layout({ children }) {
|
||||
[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
|
||||
if (
|
||||
intendedWorkspaceId != "heroku" &&
|
||||
!userWorkspaces
|
||||
.map((workspace) => workspace._id)
|
||||
.includes(intendedWorkspaceId)
|
||||
) {
|
||||
@@ -207,7 +215,10 @@ export default function Layout({ children }) {
|
||||
workspaceMapping[workspaceSelected] +
|
||||
"?Development"
|
||||
);
|
||||
localStorage.setItem("projectData.id", workspaceMapping[workspaceSelected])
|
||||
localStorage.setItem(
|
||||
"projectData.id",
|
||||
workspaceMapping[workspaceSelected]
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
@@ -226,8 +237,8 @@ export default function Layout({ children }) {
|
||||
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
|
||||
PROJECT
|
||||
</div>
|
||||
{workspaceList.length>0
|
||||
? <Listbox
|
||||
{workspaceList.length > 0 ? (
|
||||
<Listbox
|
||||
selected={workspaceSelected}
|
||||
onChange={setWorkspaceSelected}
|
||||
data={workspaceList}
|
||||
@@ -235,58 +246,67 @@ export default function Layout({ children }) {
|
||||
text=""
|
||||
workspaceMapping={workspaceMapping}
|
||||
/>
|
||||
: <Button
|
||||
) : (
|
||||
<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}>
|
||||
{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 hover:bg-mineshaft-50/5`}
|
||||
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>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
) : (
|
||||
<Link href={href}>
|
||||
<div
|
||||
className={`flex p-2 text-white text-sm rounded cursor-pointer hover:bg-mineshaft-50/5`}
|
||||
>
|
||||
<p className="ml-2 mr-4">
|
||||
{emoji}
|
||||
</p>
|
||||
{title}
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import React from "react";
|
||||
import { faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faXmark,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
export default function BottonRightPopup({
|
||||
buttonText,
|
||||
@@ -11,22 +9,34 @@ export default function BottonRightPopup({
|
||||
emoji,
|
||||
textLine1,
|
||||
textLine2,
|
||||
setCheckDocsPopUpVisible
|
||||
setCheckDocsPopUpVisible,
|
||||
}) {
|
||||
return (
|
||||
<div class="z-50 drop-shadow-xl border-gray-600/50 border flex flex-col items-start bg-bunker max-w-xl text-gray-200 pt-3 pb-4 rounded-xl absolute bottom-0 right-0 mr-6 mb-6" role="alert">
|
||||
<div
|
||||
class="z-50 drop-shadow-xl border-gray-600/50 border flex flex-col items-start bg-bunker max-w-xl text-gray-200 pt-3 pb-4 rounded-xl absolute bottom-0 right-0 mr-6 mb-6"
|
||||
role="alert"
|
||||
>
|
||||
<div className="flex flex-row items-center justify-between w-full border-b border-gray-600/70 pb-3 px-6">
|
||||
<div className="font-bold text-xl mr-2 mt-0.5 flex flex-row">
|
||||
<div>{titleText}</div>
|
||||
<div class="ml-2.5">{emoji}</div>
|
||||
</div>
|
||||
<button className="mt-1" onClick={() => setCheckDocsPopUpVisible(false)}>
|
||||
<FontAwesomeIcon icon={faXmark} className="text-gray-400 text-2xl hover:text-red duration-200 cursor-pointer" />
|
||||
<button
|
||||
className="mt-1"
|
||||
onClick={() => setCheckDocsPopUpVisible(false)}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faXmark}
|
||||
className="text-gray-400 text-2xl hover:text-red duration-200 cursor-pointer"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="block sm:inline px-6 mt-4 mb-0.5 text-gray-300">{textLine1}</div>
|
||||
<div class="block sm:inline px-6 mt-4 mb-0.5 text-gray-300">
|
||||
{textLine1}
|
||||
</div>
|
||||
<div class="block sm:inline mb-4 px-6">{textLine2}</div>
|
||||
<div className="flex flex-row px-6 w-full">
|
||||
{/*eslint-disable-next-line react/jsx-no-target-blank */}
|
||||
<a
|
||||
class="font-bold p-2 bg-white/10 rounded-md w-full hover:bg-primary duration-200 hover:text-black flex justify-center"
|
||||
href={buttonLink}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { faX } from "@fortawesome/free-solid-svg-icons";
|
||||
import Button from "../buttons/Button";
|
||||
|
||||
import guidGenerator from "../../utilities/randomId";
|
||||
import Button from "../buttons/Button";
|
||||
|
||||
const roles = ["admin", "user"];
|
||||
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import Listbox from "../Listbox";
|
||||
import uploadKeys from "../../../pages/api/workspace/uploadKeys";
|
||||
import getLatestFileKey from "../../../pages/api/workspace/getLatestFileKey";
|
||||
import deleteUserFromWorkspace from "../../../pages/api/workspace/deleteUserFromWorkspace";
|
||||
import changeUserRoleInWorkspace from "../../../pages/api/workspace/changeUserRoleInWorkspace";
|
||||
import deleteUserFromOrganization from "../../../pages/api/organization/deleteUserFromOrganization";
|
||||
import { faX } from "@fortawesome/free-solid-svg-icons";
|
||||
import Button from "../buttons/Button";
|
||||
|
||||
import deleteUserFromOrganization from "~/pages/api/organization/deleteUserFromOrganization";
|
||||
import changeUserRoleInWorkspace from "~/pages/api/workspace/changeUserRoleInWorkspace";
|
||||
import deleteUserFromWorkspace from "~/pages/api/workspace/deleteUserFromWorkspace";
|
||||
import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey";
|
||||
import uploadKeys from "~/pages/api/workspace/uploadKeys";
|
||||
|
||||
import guidGenerator from "../../utilities/randomId";
|
||||
import Button from "../buttons/Button";
|
||||
import Listbox from "../Listbox";
|
||||
|
||||
const {
|
||||
decryptAssymmetric,
|
||||
@@ -81,7 +83,7 @@ const UserTable = ({
|
||||
]);
|
||||
};
|
||||
|
||||
useEffect(async () => {
|
||||
useEffect(() => {
|
||||
setMyRole(userData.filter((user) => user.email == myUser)[0]?.role);
|
||||
}, [userData, myUser]);
|
||||
|
||||
@@ -145,7 +147,10 @@ const UserTable = ({
|
||||
)
|
||||
.map((row, index) => {
|
||||
return (
|
||||
<tr key={guidGenerator()} className="bg-bunker-800 hover:bg-bunker-800/5">
|
||||
<tr
|
||||
key={guidGenerator()}
|
||||
className="bg-bunker-800 hover:bg-bunker-800/5"
|
||||
>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{row.firstName}
|
||||
</td>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from "react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faCheck, faQuestionCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faCcMastercard, faCcVisa } from "@fortawesome/free-brands-svg-icons";
|
||||
import { faCheck, faQuestionCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
export default function Card({ card, changeSelectedCard, selected }) {
|
||||
function creditCardBrandIcon(cc) {
|
||||
@@ -37,24 +37,27 @@ export default function Card({ card, changeSelectedCard, selected }) {
|
||||
<div className="flex flex-row items-center justify-between w-full">
|
||||
<div className="flex flex-row items-center">
|
||||
<p className="tracking-tighter mr-1 mt-0.5 flex">
|
||||
{"****".split("").map(() => (
|
||||
{"****".split("").map((_, index) => (
|
||||
<FontAwesomeIcon
|
||||
key={index}
|
||||
className="text-xxxs mr-0.5"
|
||||
icon={faCircle}
|
||||
/>
|
||||
))}
|
||||
</p>
|
||||
<p className="tracking-tighter mr-1 mt-0.5 flex">
|
||||
{"****".split("").map(() => (
|
||||
{"****".split("").map((_, index) => (
|
||||
<FontAwesomeIcon
|
||||
key={index}
|
||||
className="text-xxxs mr-0.5"
|
||||
icon={faCircle}
|
||||
/>
|
||||
))}
|
||||
</p>
|
||||
<p className="tracking-tighter mr-1 mt-0.5 flex">
|
||||
{"****".split("").map(() => (
|
||||
{"****".split("").map((_, index) => (
|
||||
<FontAwesomeIcon
|
||||
key={index}
|
||||
className="text-xxxs mr-0.5"
|
||||
icon={faCircle}
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from "react";
|
||||
import StripeRedirect from "../../pages/api/organization/StripeRedirect";
|
||||
|
||||
import StripeRedirect from "~/pages/api/organization/StripeRedirect";
|
||||
|
||||
export default function Plan({ plan }) {
|
||||
return (
|
||||
@@ -27,12 +28,12 @@ export default function Plan({ plan }) {
|
||||
{plan.priceExplanation}
|
||||
</p>
|
||||
</div>
|
||||
<p className="relative z-10 max-w-fit px-6 text-base text-gray-400">
|
||||
{plan.text}
|
||||
</p>
|
||||
<p className="relative z-10 max-w-fit px-6 text-base text-gray-400">
|
||||
{plan.subtext}
|
||||
</p>
|
||||
<p className="relative z-10 max-w-fit px-6 text-base text-gray-400">
|
||||
{plan.text}
|
||||
</p>
|
||||
<p className="relative z-10 max-w-fit px-6 text-base text-gray-400">
|
||||
{plan.subtext}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-row items-center">
|
||||
{plan.current == false ? (
|
||||
@@ -54,7 +55,17 @@ export default function Plan({ plan }) {
|
||||
: "hover:bg-primary hover:text-black hover:border-primary"
|
||||
} bg-bunker duration-200 cursor-pointer rounded-md flex w-max`}
|
||||
>
|
||||
<button onClick={() => StripeRedirect({orgId: localStorage.getItem("orgData.id")})}>{plan.buttonTextMain}</button>
|
||||
<button
|
||||
onClick={() =>
|
||||
StripeRedirect({
|
||||
orgId: localStorage.getItem(
|
||||
"orgData.id"
|
||||
),
|
||||
})
|
||||
}
|
||||
>
|
||||
{plan.buttonTextMain}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<a href="/pricing" target='_blank rel="noopener"'>
|
||||
|
||||
@@ -1,39 +1,45 @@
|
||||
import React from "react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import React, { Fragment } from "react";
|
||||
import { faCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import guidGenerator from "../utilities/randomId";
|
||||
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
|
||||
* @returns
|
||||
*/
|
||||
const findReferences = (text) => {
|
||||
var splitText = text.split('${');
|
||||
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 += "}"
|
||||
let insideBrackets = "${" + splitText[i].split("}")[0];
|
||||
if (splitText[i].includes("}")) {
|
||||
insideBrackets += "}";
|
||||
}
|
||||
textArray.push(insideBrackets)
|
||||
textArray.push(splitText[i].split('}')[1])
|
||||
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 {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 {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
|
||||
* @returns
|
||||
*/
|
||||
const DashboardInputField = ({index, onChangeHandler, type, value, blurred}) => {
|
||||
const DashboardInputField = ({
|
||||
index,
|
||||
onChangeHandler,
|
||||
type,
|
||||
value,
|
||||
blurred,
|
||||
}) => {
|
||||
if (type === "varName") {
|
||||
return (
|
||||
<div className="flex-col w-full">
|
||||
@@ -67,32 +73,58 @@ const DashboardInputField = ({index, onChangeHandler, type, value, blurred}) =>
|
||||
} 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
|
||||
<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 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((text, id) =>
|
||||
id % 2 == 0 || text.length <= 2
|
||||
? <span className="ph-no-capture">{text}</span>
|
||||
: <span className="ph-no-capture text-yellow">
|
||||
{text.slice(0, 2)}
|
||||
<span className="ph-no-capture text-yellow-200/80">
|
||||
{text.slice(2, text.length - 1)}
|
||||
{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>
|
||||
{text.slice(text.length - 1, text.length) == "}"
|
||||
? <span className="ph-no-capture text-yellow">{text.slice(text.length - 1, text.length)} </span>
|
||||
: <span className="ph-no-capture text-yellow-400">{text.slice(text.length - 1, text.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)
|
||||
.slice(0, 42)
|
||||
.map(() => (
|
||||
<FontAwesomeIcon
|
||||
key={guidGenerator()}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { useState } from "react";
|
||||
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faUpload } from "@fortawesome/free-solid-svg-icons";
|
||||
import guidGenerator from "../utilities/randomId";
|
||||
|
||||
import Error from "../basic/Error";
|
||||
import Image from "next/image";
|
||||
import parse from "../utilities/file";
|
||||
import { faUpload } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import Button from "../basic/buttons/Button";
|
||||
import Error from "../basic/Error";
|
||||
import parse from "../utilities/file";
|
||||
import guidGenerator from "../utilities/randomId";
|
||||
|
||||
const DropZone = ({
|
||||
setData,
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
import React, { useState, Fragment, useEffect } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
/* eslint-disable react/jsx-key */
|
||||
import React, { Fragment, useEffect,useState } from "react";
|
||||
import Image from "next/image";
|
||||
|
||||
import logout from "../../pages/api/auth/Logout";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useRouter } from "next/router";
|
||||
import { faGithub,faSlack } from "@fortawesome/free-brands-svg-icons";
|
||||
import { faCircleQuestion } from "@fortawesome/free-regular-svg-icons";
|
||||
import {
|
||||
faAngleDown,
|
||||
faBook,
|
||||
faGear,
|
||||
faCoins,
|
||||
faRightFromBracket,
|
||||
faEnvelope,
|
||||
faPlus,
|
||||
faAngleDown
|
||||
faGear,
|
||||
faPlus,
|
||||
faRightFromBracket,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { faSlack, faGithub } from "@fortawesome/free-brands-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Menu, Transition } from "@headlessui/react";
|
||||
import getUser from "../../pages/api/user/getUser";
|
||||
import getOrganizations from "../../pages/api/organization/getOrgs";
|
||||
import getOrganization from "../../pages/api/organization/GetOrg";
|
||||
|
||||
import logout from "~/pages/api/auth/Logout";
|
||||
import getOrganization from "~/pages/api/organization/GetOrg";
|
||||
import getOrganizations from "~/pages/api/organization/getOrgs";
|
||||
import getUser from "~/pages/api/user/getUser";
|
||||
|
||||
import guidGenerator from "../utilities/randomId";
|
||||
|
||||
const supportOptions = [
|
||||
@@ -87,7 +89,7 @@ export default function Navbar({ onButtonPressed }) {
|
||||
</div>
|
||||
<div className="relative flex justify-start items-center mx-2 z-40">
|
||||
<Menu as="div" className="relative inline-block text-left">
|
||||
<div className="mr-4">
|
||||
<div className="mr-4">
|
||||
<Menu.Button className="inline-flex w-full justify-center rounded-md px-2 py-2 text-sm font-medium text-gray-200 rounded-md hover:bg-white/10 duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75">
|
||||
<FontAwesomeIcon
|
||||
className="text-xl"
|
||||
@@ -106,6 +108,7 @@ export default function Navbar({ onButtonPressed }) {
|
||||
>
|
||||
<Menu.Items className="absolute right-0 mt-0.5 w-64 origin-top-right rounded-md bg-bunker border border-mineshaft-700 shadow-lg ring-1 ring-black z-20 ring-opacity-5 focus:outline-none px-2 py-1.5">
|
||||
{supportOptions.map((option) => (
|
||||
// eslint-disable-next-line react/jsx-no-target-blank
|
||||
<a
|
||||
key={guidGenerator()}
|
||||
target="_blank"
|
||||
@@ -128,7 +131,10 @@ export default function Navbar({ onButtonPressed }) {
|
||||
<div>
|
||||
<Menu.Button className="inline-flex w-full justify-center rounded-md pr-2 pl-2 py-2 text-sm font-medium text-gray-200 rounded-md hover:bg-white/10 duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75">
|
||||
{user?.firstName} {user?.lastName}
|
||||
<FontAwesomeIcon icon={faAngleDown} className="ml-2 mt-1 text-sm text-gray-300 hover:text-lime-100"/>
|
||||
<FontAwesomeIcon
|
||||
icon={faAngleDown}
|
||||
className="ml-2 mt-1 text-sm text-gray-300 hover:text-lime-100"
|
||||
/>
|
||||
</Menu.Button>
|
||||
</div>
|
||||
<Transition
|
||||
@@ -145,13 +151,15 @@ export default function Navbar({ onButtonPressed }) {
|
||||
<div className="text-gray-400 self-start ml-2 mt-2 text-xs font-semibold tracking-wide">
|
||||
SIGNED IN AS
|
||||
</div>
|
||||
<div
|
||||
<div
|
||||
onClick={() =>
|
||||
router.push(
|
||||
"/settings/personal/" + router.query.id
|
||||
"/settings/personal/" +
|
||||
router.query.id
|
||||
)
|
||||
}
|
||||
className="flex flex-row items-center px-1 mx-1 my-1 hover:bg-white/5 cursor-pointer rounded-md">
|
||||
className="flex flex-row items-center px-1 mx-1 my-1 hover:bg-white/5 cursor-pointer rounded-md"
|
||||
>
|
||||
<div className="bg-white/10 h-8 w-9 rounded-full flex items-center justify-center text-gray-300">
|
||||
{user?.firstName?.charAt(0)}
|
||||
</div>
|
||||
@@ -159,7 +167,8 @@ export default function Navbar({ onButtonPressed }) {
|
||||
<div>
|
||||
<p className="text-gray-300 px-2 pt-1 text-sm">
|
||||
{" "}
|
||||
{user?.firstName} {user?.lastName}
|
||||
{user?.firstName}{" "}
|
||||
{user?.lastName}
|
||||
</p>
|
||||
<p className="text-gray-400 px-2 pb-1 text-xs">
|
||||
{" "}
|
||||
@@ -235,7 +244,10 @@ export default function Navbar({ onButtonPressed }) {
|
||||
className="relative flex justify-start cursor-pointer select-none py-2 pl-10 pr-4 rounded-md text-gray-400 hover:bg-primary/100 duration-200 hover:text-black hover:font-semibold mt-1"
|
||||
>
|
||||
<span className="rounded-lg absolute inset-y-0 left-0 flex items-center pl-3 pr-4">
|
||||
<FontAwesomeIcon icon={faPlus} className="ml-1" />
|
||||
<FontAwesomeIcon
|
||||
icon={faPlus}
|
||||
className="ml-1"
|
||||
/>
|
||||
</span>
|
||||
<div className="text-sm ml-1">
|
||||
Invite Members
|
||||
@@ -243,43 +255,45 @@ export default function Navbar({ onButtonPressed }) {
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
{orgs?.length > 1 && <div className="px-1 pt-1">
|
||||
<div className="text-gray-400 self-start ml-2 mt-2 text-xs font-semibold tracking-wide">
|
||||
OTHER ORGANIZATIONS
|
||||
</div>
|
||||
<div className="flex flex-col items-start px-1 mt-3 mb-2">
|
||||
{orgs
|
||||
.filter(
|
||||
(org) =>
|
||||
org._id !=
|
||||
localStorage.getItem(
|
||||
"orgData.id"
|
||||
)
|
||||
)
|
||||
.map((org) => (
|
||||
<div
|
||||
key={guidGenerator()}
|
||||
onClick={() => {
|
||||
localStorage.setItem(
|
||||
"orgData.id",
|
||||
org._id
|
||||
);
|
||||
router.reload();
|
||||
}}
|
||||
className="flex flex-row justify-start items-center hover:bg-white/5 w-full p-1.5 cursor-pointer rounded-md"
|
||||
>
|
||||
<div className="bg-white/10 h-7 w-8 rounded-md flex items-center justify-center text-gray-300">
|
||||
{org.name.charAt(0)}
|
||||
{orgs?.length > 1 && (
|
||||
<div className="px-1 pt-1">
|
||||
<div className="text-gray-400 self-start ml-2 mt-2 text-xs font-semibold tracking-wide">
|
||||
OTHER ORGANIZATIONS
|
||||
</div>
|
||||
<div className="flex flex-col items-start px-1 mt-3 mb-2">
|
||||
{orgs
|
||||
.filter(
|
||||
(org) =>
|
||||
org._id !=
|
||||
localStorage.getItem(
|
||||
"orgData.id"
|
||||
)
|
||||
)
|
||||
.map((org) => (
|
||||
<div
|
||||
key={guidGenerator()}
|
||||
onClick={() => {
|
||||
localStorage.setItem(
|
||||
"orgData.id",
|
||||
org._id
|
||||
);
|
||||
router.reload();
|
||||
}}
|
||||
className="flex flex-row justify-start items-center hover:bg-white/5 w-full p-1.5 cursor-pointer rounded-md"
|
||||
>
|
||||
<div className="bg-white/10 h-7 w-8 rounded-md flex items-center justify-center text-gray-300">
|
||||
{org.name.charAt(0)}
|
||||
</div>
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<p className="text-gray-300 px-2 text-sm">
|
||||
{org.name}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<p className="text-gray-300 px-2 text-sm">
|
||||
{org.name}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>}
|
||||
)}
|
||||
<div className="px-1 py-1">
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
|
||||
@@ -1,47 +1,57 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faAngleRight, faQuestionCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faCcMastercard, faCcVisa } from "@fortawesome/free-brands-svg-icons";
|
||||
import { faCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import getOrganization from "../../pages/api/organization/GetOrg";
|
||||
import getWorkspaceInfo from "../../pages/api/workspace/getWorkspaceInfo";
|
||||
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";
|
||||
|
||||
export default function NavHeader({ pageName, isProjectRelated}) {
|
||||
const [orgName, setOrgName] = useState("");
|
||||
const [workspaceName, setWorkspaceName] = useState("");
|
||||
const router = useRouter();
|
||||
import getOrganization from "~/pages/api/organization/GetOrg";
|
||||
import getWorkspaceInfo from "~/pages/api/workspace/getWorkspaceInfo";
|
||||
|
||||
useEffect(async () => {
|
||||
let org = await getOrganization({
|
||||
orgId: localStorage.getItem("orgData.id"),
|
||||
});
|
||||
setOrgName(org.name);
|
||||
let workspace = await getWorkspaceInfo({
|
||||
workspaceId: router.query.id,
|
||||
});
|
||||
setWorkspaceName(workspace.name);
|
||||
}, []);
|
||||
export default function NavHeader({ pageName, isProjectRelated }) {
|
||||
const [orgName, setOrgName] = useState("");
|
||||
const [workspaceName, setWorkspaceName] = useState("");
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
let org = await getOrganization({
|
||||
orgId: localStorage.getItem("orgData.id"),
|
||||
});
|
||||
setOrgName(org.name);
|
||||
let workspace = await getWorkspaceInfo({
|
||||
workspaceId: router.query.id,
|
||||
});
|
||||
setWorkspaceName(workspace.name);
|
||||
})();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="pt-20 ml-6 flex flex-row items-center">
|
||||
<div className="bg-primary-900 h-6 w-6 rounded-md flex items-center justify-center text-mineshaft-100 mr-2">
|
||||
{orgName?.charAt(0)}
|
||||
</div>
|
||||
<div className="text-primary text-sm font-semibold">
|
||||
{orgName}
|
||||
</div>
|
||||
{isProjectRelated && <>
|
||||
<FontAwesomeIcon icon={faAngleRight} className="ml-3 text-sm text-gray-400 mr-3" />
|
||||
<div className="font-semibold text-primary text-sm">
|
||||
{workspaceName}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
<FontAwesomeIcon icon={faAngleRight} className="ml-3 text-sm text-gray-400 mr-3" />
|
||||
<div className="text-gray-400 text-sm">
|
||||
{pageName}
|
||||
</div>
|
||||
</div>
|
||||
<div className="pt-20 ml-6 flex flex-row items-center">
|
||||
<div className="bg-primary-900 h-6 w-6 rounded-md flex items-center justify-center text-mineshaft-100 mr-2">
|
||||
{orgName?.charAt(0)}
|
||||
</div>
|
||||
<div className="text-primary text-sm font-semibold">{orgName}</div>
|
||||
{isProjectRelated && (
|
||||
<>
|
||||
<FontAwesomeIcon
|
||||
icon={faAngleRight}
|
||||
className="ml-3 text-sm text-gray-400 mr-3"
|
||||
/>
|
||||
<div className="font-semibold text-primary text-sm">
|
||||
{workspaceName}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<FontAwesomeIcon
|
||||
icon={faAngleRight}
|
||||
className="ml-3 text-sm text-gray-400 mr-3"
|
||||
/>
|
||||
<div className="text-gray-400 text-sm">{pageName}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,29 +1,28 @@
|
||||
import token from "../../pages/api/auth/Token"
|
||||
import { PATH } from '../../const';
|
||||
import token from "~/pages/api/auth/Token";
|
||||
|
||||
import { PATH } from "../../const";
|
||||
|
||||
export default class SecurityClient {
|
||||
static authOrigins = [PATH]
|
||||
static #token = '';
|
||||
static authOrigins = [PATH];
|
||||
static #token = "";
|
||||
|
||||
contructor() {
|
||||
contructor() {}
|
||||
|
||||
}
|
||||
static setToken(token) {
|
||||
this.#token = token;
|
||||
}
|
||||
|
||||
static setToken(token) {
|
||||
this.#token = token;
|
||||
}
|
||||
static async fetchCall(resource, options) {
|
||||
let req = new Request(resource, options);
|
||||
const destOrigin = new URL(req.url).origin;
|
||||
|
||||
static async fetchCall(resource, options) {
|
||||
let req = new Request(resource, options);
|
||||
const destOrigin = new URL(req.url).origin;
|
||||
if (this.#token == "") {
|
||||
this.setToken(await token());
|
||||
}
|
||||
|
||||
if (this.#token == "") {
|
||||
this.setToken(await token())
|
||||
}
|
||||
|
||||
if (this.#token && this.authOrigins.includes(destOrigin)) {
|
||||
req.headers.set('Authorization', "Bearer " + this.#token);
|
||||
return fetch(req);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.#token && this.authOrigins.includes(destOrigin)) {
|
||||
req.headers.set("Authorization", "Bearer " + this.#token);
|
||||
return fetch(req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import login1 from "../../pages/api/auth/Login1";
|
||||
import login2 from "../../pages/api/auth/Login2";
|
||||
import Aes256Gcm from "../../components/aes-256-gcm";
|
||||
import pushKeys from "./pushKeys";
|
||||
import Aes256Gcm from "~/components/aes-256-gcm";
|
||||
import login1 from "~/pages/api/auth/Login1";
|
||||
import login2 from "~/pages/api/auth/Login2";
|
||||
import getOrganizations from "~/pages/api/organization/getOrgs";
|
||||
import getOrganizationUserProjects from "~/pages/api/organization/GetOrgUserProjects";
|
||||
|
||||
import { initPostHog } from "../analytics/posthog";
|
||||
import getOrganizations from "../../pages/api/organization/getOrgs";
|
||||
import getOrganizationUserProjects from "../../pages/api/organization/GetOrgUserProjects";
|
||||
import SecurityClient from "./SecurityClient";
|
||||
import { ENV } from "./config";
|
||||
import pushKeys from "./pushKeys";
|
||||
import SecurityClient from "./SecurityClient";
|
||||
|
||||
const nacl = require("tweetnacl");
|
||||
nacl.util = require("tweetnacl-util");
|
||||
@@ -77,7 +78,14 @@ const attemptLogin = async (
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
password.slice(0, 32).padStart(32 + (password.slice(0, 32).length - new Blob([password]).size), "0")
|
||||
password
|
||||
.slice(0, 32)
|
||||
.padStart(
|
||||
32 +
|
||||
(password.slice(0, 32).length -
|
||||
new Blob([password]).size),
|
||||
"0"
|
||||
)
|
||||
);
|
||||
|
||||
try {
|
||||
@@ -101,10 +109,14 @@ const attemptLogin = async (
|
||||
}
|
||||
|
||||
const userOrgs = await getOrganizations();
|
||||
const userOrgsData = userOrgs.map(org => org._id);
|
||||
|
||||
const userOrgsData = userOrgs.map((org) => org._id);
|
||||
|
||||
let orgToLogin;
|
||||
if (userOrgsData.includes(localStorage.getItem("orgData.id"))) {
|
||||
if (
|
||||
userOrgsData.includes(
|
||||
localStorage.getItem("orgData.id")
|
||||
)
|
||||
) {
|
||||
orgToLogin = localStorage.getItem("orgData.id");
|
||||
} else {
|
||||
orgToLogin = userOrgsData[0];
|
||||
@@ -114,17 +126,29 @@ const attemptLogin = async (
|
||||
let orgUserProjects = await getOrganizationUserProjects({
|
||||
orgId: orgToLogin,
|
||||
});
|
||||
|
||||
orgUserProjects = orgUserProjects?.map(project => project._id);
|
||||
|
||||
orgUserProjects = orgUserProjects?.map(
|
||||
(project) => project._id
|
||||
);
|
||||
let projectToLogin;
|
||||
if (orgUserProjects.includes(localStorage.getItem("projectData.id"))) {
|
||||
if (
|
||||
orgUserProjects.includes(
|
||||
localStorage.getItem("projectData.id")
|
||||
)
|
||||
) {
|
||||
projectToLogin = localStorage.getItem("projectData.id");
|
||||
} else {
|
||||
try {
|
||||
projectToLogin = orgUserProjects[0];
|
||||
localStorage.setItem("projectData.id", projectToLogin);
|
||||
localStorage.setItem(
|
||||
"projectData.id",
|
||||
projectToLogin
|
||||
);
|
||||
} catch (error) {
|
||||
console.log("ERROR: User likely has no projects. ", error)
|
||||
console.log(
|
||||
"ERROR: User likely has no projects. ",
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import changePassword2 from "~/pages/api/auth/ChangePassword2";
|
||||
import SRP1 from "~/pages/api/auth/SRP1";
|
||||
|
||||
import Aes256Gcm from "../aes-256-gcm";
|
||||
import SRP1 from "../../pages/api/auth/SRP1";
|
||||
import changePassword2 from "../../pages/api/auth/ChangePassword2";
|
||||
|
||||
const nacl = require("tweetnacl");
|
||||
nacl.util = require("tweetnacl-util");
|
||||
@@ -41,7 +42,7 @@ const changePassword = async (
|
||||
let serverPublicKey, salt;
|
||||
try {
|
||||
const res = await SRP1({
|
||||
clientPublicKey: clientPublicKey
|
||||
clientPublicKey: clientPublicKey,
|
||||
});
|
||||
serverPublicKey = res.serverPublicKey;
|
||||
salt = res.salt;
|
||||
@@ -54,54 +55,70 @@ const changePassword = async (
|
||||
clientOldPassword.setServerPublicKey(serverPublicKey);
|
||||
const clientProof = clientOldPassword.getProof(); // called M1
|
||||
|
||||
clientNewPassword.init({
|
||||
username: email,
|
||||
password: newPassword
|
||||
}, async () => {
|
||||
clientNewPassword.createVerifier(async (err, result) => {
|
||||
|
||||
let { ciphertext, iv, tag } = Aes256Gcm.encrypt(
|
||||
localStorage.getItem("PRIVATE_KEY"),
|
||||
newPassword.slice(0, 32).padStart(32 + (newPassword.slice(0, 32).length - new Blob([newPassword]).size), "0")
|
||||
);
|
||||
clientNewPassword.init(
|
||||
{
|
||||
username: email,
|
||||
password: newPassword,
|
||||
},
|
||||
async () => {
|
||||
clientNewPassword.createVerifier(
|
||||
async (err, result) => {
|
||||
let { ciphertext, iv, tag } = Aes256Gcm.encrypt(
|
||||
localStorage.getItem("PRIVATE_KEY"),
|
||||
newPassword
|
||||
.slice(0, 32)
|
||||
.padStart(
|
||||
32 +
|
||||
(newPassword.slice(0, 32)
|
||||
.length -
|
||||
new Blob([newPassword])
|
||||
.size),
|
||||
"0"
|
||||
)
|
||||
);
|
||||
|
||||
if (ciphertext) {
|
||||
localStorage.setItem(
|
||||
"encryptedPrivateKey",
|
||||
ciphertext
|
||||
);
|
||||
localStorage.setItem("iv", iv);
|
||||
localStorage.setItem("tag", tag);
|
||||
|
||||
let res;
|
||||
try {
|
||||
res = await changePassword2({
|
||||
encryptedPrivateKey: ciphertext,
|
||||
iv,
|
||||
tag,
|
||||
salt: result.salt,
|
||||
verifier: result.verifier,
|
||||
clientProof
|
||||
});
|
||||
if (res.status == 400) {
|
||||
setCurrentPasswordError(true);
|
||||
} else if (res.status == 200) {
|
||||
setPasswordChanged(true);
|
||||
setCurrentPassword("");
|
||||
setNewPassword("");
|
||||
if (ciphertext) {
|
||||
localStorage.setItem(
|
||||
"encryptedPrivateKey",
|
||||
ciphertext
|
||||
);
|
||||
localStorage.setItem("iv", iv);
|
||||
localStorage.setItem("tag", tag);
|
||||
|
||||
let res;
|
||||
try {
|
||||
res = await changePassword2({
|
||||
encryptedPrivateKey: ciphertext,
|
||||
iv,
|
||||
tag,
|
||||
salt: result.salt,
|
||||
verifier: result.verifier,
|
||||
clientProof,
|
||||
});
|
||||
if (res.status == 400) {
|
||||
setCurrentPasswordError(true);
|
||||
} else if (res.status == 200) {
|
||||
setPasswordChanged(true);
|
||||
setCurrentPassword("");
|
||||
setNewPassword("");
|
||||
}
|
||||
} catch (err) {
|
||||
setCurrentPasswordError(true);
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
setCurrentPasswordError(true)
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.log("Something went wrong during changing the password", slat, serverPublicKey);
|
||||
console.log(
|
||||
"Something went wrong during changing the password",
|
||||
slat,
|
||||
serverPublicKey
|
||||
);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import getSecrets from "../../pages/api/files/GetSecrets";
|
||||
import guidGenerator from "./randomId";
|
||||
import getSecrets from "~/pages/api/files/GetSecrets";
|
||||
|
||||
import guidGenerator from "./randomId";
|
||||
|
||||
const {
|
||||
decryptAssymmetric,
|
||||
@@ -21,15 +21,12 @@ const getSecretsForProject = async ({
|
||||
setFileState,
|
||||
setIsKeyAvailable,
|
||||
setData,
|
||||
workspaceId
|
||||
workspaceId,
|
||||
}) => {
|
||||
try {
|
||||
let file;
|
||||
try {
|
||||
file = await getSecrets(
|
||||
workspaceId,
|
||||
envMapping[env]
|
||||
);
|
||||
file = await getSecrets(workspaceId, envMapping[env]);
|
||||
|
||||
setFileState(file);
|
||||
} catch (error) {
|
||||
@@ -98,7 +95,9 @@ const getSecretsForProject = async ({
|
||||
line["type"],
|
||||
]);
|
||||
} catch (error) {
|
||||
console.log("Something went wrong during accessing or decripting secrets.");
|
||||
console.log(
|
||||
"Something went wrong during accessing or decripting secrets."
|
||||
);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import issueBackupPrivateKey from "~/pages/api/auth/IssueBackupPrivateKey";
|
||||
import SRP1 from "~/pages/api/auth/SRP1";
|
||||
|
||||
import Aes256Gcm from "../aes-256-gcm";
|
||||
import SRP1 from "../../pages/api/auth/SRP1";
|
||||
import issueBackupPrivateKey from "../../pages/api/auth/IssueBackupPrivateKey";
|
||||
import generateBackupPDF from "./generateBackupPDF";
|
||||
|
||||
const nacl = require("tweetnacl");
|
||||
@@ -24,7 +25,7 @@ const issueBackupKey = async ({
|
||||
password,
|
||||
personalName,
|
||||
setBackupKeyError,
|
||||
setBackupKeyIssued
|
||||
setBackupKeyIssued,
|
||||
}) => {
|
||||
try {
|
||||
setBackupKeyError(false);
|
||||
@@ -40,7 +41,7 @@ const issueBackupKey = async ({
|
||||
let serverPublicKey, salt;
|
||||
try {
|
||||
const res = await SRP1({
|
||||
clientPublicKey: clientPublicKey
|
||||
clientPublicKey: clientPublicKey,
|
||||
});
|
||||
serverPublicKey = res.serverPublicKey;
|
||||
salt = res.salt;
|
||||
@@ -55,35 +56,40 @@ const issueBackupKey = async ({
|
||||
|
||||
const generatedKey = crypto.randomBytes(16).toString("hex");
|
||||
|
||||
clientKey.init({
|
||||
username: email,
|
||||
password: generatedKey
|
||||
}, async () => {
|
||||
clientKey.createVerifier(async (err, result) => {
|
||||
clientKey.init(
|
||||
{
|
||||
username: email,
|
||||
password: generatedKey,
|
||||
},
|
||||
async () => {
|
||||
clientKey.createVerifier(async (err, result) => {
|
||||
let { ciphertext, iv, tag } = Aes256Gcm.encrypt(
|
||||
localStorage.getItem("PRIVATE_KEY"),
|
||||
generatedKey
|
||||
);
|
||||
|
||||
const res = await issueBackupPrivateKey({
|
||||
encryptedPrivateKey: ciphertext,
|
||||
iv,
|
||||
tag,
|
||||
salt: result.salt,
|
||||
verifier: result.verifier,
|
||||
clientProof,
|
||||
});
|
||||
|
||||
let { ciphertext, iv, tag } = Aes256Gcm.encrypt(
|
||||
localStorage.getItem("PRIVATE_KEY"),
|
||||
generatedKey
|
||||
);
|
||||
|
||||
const res = await issueBackupPrivateKey({
|
||||
encryptedPrivateKey: ciphertext,
|
||||
iv,
|
||||
tag,
|
||||
salt: result.salt,
|
||||
verifier: result.verifier,
|
||||
clientProof
|
||||
if (res.status == 400) {
|
||||
setBackupKeyError(true);
|
||||
} else if (res.status == 200) {
|
||||
generateBackupPDF(
|
||||
personalName,
|
||||
email,
|
||||
generatedKey
|
||||
);
|
||||
setBackupKeyIssued(true);
|
||||
}
|
||||
});
|
||||
|
||||
if (res.status == 400) {
|
||||
setBackupKeyError(true);
|
||||
} else if (res.status == 200) {
|
||||
generateBackupPDF(personalName, email, generatedKey);
|
||||
setBackupKeyIssued(true);
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import getLatestFileKey from "../../pages/api/workspace/getLatestFileKey";
|
||||
import getWorkspaceKeys from "../../pages/api/workspace/getWorkspaceKeys";
|
||||
import uploadSecrets from "../../pages/api/files/UploadSecrets";
|
||||
import uploadSecrets from "~/pages/api/files/UploadSecrets";
|
||||
import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey";
|
||||
import getWorkspaceKeys from "~/pages/api/workspace/getWorkspaceKeys";
|
||||
|
||||
const crypto = require("crypto");
|
||||
const {
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import publicKeyInfical from "../../pages/api/auth/publicKeyInfisical"
|
||||
import changeHerokuConfigVars from "../../pages/api/integrations/ChangeHerokuConfigVars";
|
||||
import publicKeyInfical from "~/pages/api/auth/publicKeyInfisical";
|
||||
import changeHerokuConfigVars from "~/pages/api/integrations/ChangeHerokuConfigVars";
|
||||
|
||||
const crypto = require("crypto");
|
||||
const {
|
||||
encryptSymmetric,
|
||||
encryptAssymmetric,
|
||||
} = require("./crypto");
|
||||
const { encryptSymmetric, encryptAssymmetric } = require("./crypto");
|
||||
const nacl = require("tweetnacl");
|
||||
nacl.util = require("tweetnacl-util");
|
||||
|
||||
@@ -71,7 +68,7 @@ const pushKeysIntegration = async ({ obj, integrationId }) => {
|
||||
nonce,
|
||||
};
|
||||
|
||||
changeHerokuConfigVars({integrationId, key, secrets})
|
||||
changeHerokuConfigVars({ integrationId, key, secrets });
|
||||
};
|
||||
|
||||
export default pushKeysIntegration;
|
||||
|
||||
16
frontend/jsconfig.json
Normal file
16
frontend/jsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/components/*": [
|
||||
"components/*"
|
||||
],
|
||||
"~/utilities/*": [
|
||||
"components/utilities/*"
|
||||
],
|
||||
"~/pages/*": [
|
||||
"pages/*"
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,8 @@
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"start:docker": "next build && next start"
|
||||
"start:docker": "next build && next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apihero/github": "^1.1.5",
|
||||
@@ -55,8 +56,12 @@
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.4",
|
||||
"autoprefixer": "^10.4.7",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-next": "^13.0.5",
|
||||
"eslint-plugin-simple-import-sort": "^8.0.0",
|
||||
"postcss": "^8.4.14",
|
||||
"prettier": "2.7.1",
|
||||
"tailwindcss": "^3.1.4"
|
||||
"tailwindcss": "^3.1.4",
|
||||
"typescript": "^4.9.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import "../styles/globals.css";
|
||||
import "@fortawesome/fontawesome-svg-core/styles.css";
|
||||
import { config } from "@fortawesome/fontawesome-svg-core";
|
||||
import Layout from "../components/basic/layout";
|
||||
import RouteGuard from "../components/RouteGuard";
|
||||
import { publicPaths } from "../const.js";
|
||||
import { useEffect } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { initPostHog } from "../components/analytics/posthog";
|
||||
import { ENV } from "../components/utilities/config";
|
||||
import { config } from "@fortawesome/fontawesome-svg-core";
|
||||
|
||||
import { initPostHog } from "~/components/analytics/posthog";
|
||||
import Layout from "~/components/basic/layout";
|
||||
import RouteGuard from "~/components/RouteGuard";
|
||||
import { ENV } from "~/utilities/config";
|
||||
|
||||
import { publicPaths } from "../const.js";
|
||||
|
||||
import "@fortawesome/fontawesome-svg-core/styles.css";
|
||||
import "../styles/globals.css";
|
||||
|
||||
config.autoAddCss = false;
|
||||
|
||||
@@ -39,9 +42,7 @@ const App = ({ Component, pageProps, ...appProps }) => {
|
||||
publicPaths.includes("/" + appProps.router.pathname.split("/")[1]) ||
|
||||
!Component.requireAuth
|
||||
) {
|
||||
return (
|
||||
<Component {...pageProps} />
|
||||
)
|
||||
return <Component {...pageProps} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -6,28 +7,34 @@ import { PATH } from "../../../const";
|
||||
* @param {*} clientPublicKey
|
||||
* @returns
|
||||
*/
|
||||
const changePassword2 = ({encryptedPrivateKey, iv, tag, salt, verifier, clientProof}) => {
|
||||
const changePassword2 = ({
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
salt,
|
||||
verifier,
|
||||
clientProof,
|
||||
}) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/password/change-password", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"clientProof": clientProof,
|
||||
"encryptedPrivateKey": encryptedPrivateKey,
|
||||
"iv": iv,
|
||||
"tag": tag,
|
||||
"salt": salt,
|
||||
"verifier": verifier
|
||||
clientProof: clientProof,
|
||||
encryptedPrivateKey: encryptedPrivateKey,
|
||||
iv: iv,
|
||||
tag: tag,
|
||||
salt: salt,
|
||||
verifier: verifier,
|
||||
}),
|
||||
})
|
||||
.then(async res => {
|
||||
}).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
console.log('Failed to change the password');
|
||||
console.log("Failed to change the password");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default changePassword2;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient.js";
|
||||
import SecurityClient from "~/utilities/SecurityClient.js";
|
||||
|
||||
import { PATH } from "../../../const.js";
|
||||
|
||||
/**
|
||||
@@ -14,14 +15,13 @@ const checkAuth = async (req, res) => {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(res => {
|
||||
}).then((res) => {
|
||||
if (res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
console.log("Not authorized");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default checkAuth;
|
||||
|
||||
@@ -1,32 +1,42 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
* This is the route that issues a backup private key that will afterwards be added into a pdf
|
||||
*/
|
||||
const issueBackupPrivateKey = ({encryptedPrivateKey, iv, tag, salt, verifier, clientProof}) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/password/backup-private-key", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"clientProof": clientProof,
|
||||
"encryptedPrivateKey": encryptedPrivateKey,
|
||||
"iv": iv,
|
||||
"tag": tag,
|
||||
"salt": salt,
|
||||
"verifier": verifier
|
||||
}),
|
||||
})
|
||||
.then(res => {
|
||||
const issueBackupPrivateKey = ({
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
salt,
|
||||
verifier,
|
||||
clientProof,
|
||||
}) => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/password/backup-private-key",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
clientProof: clientProof,
|
||||
encryptedPrivateKey: encryptedPrivateKey,
|
||||
iv: iv,
|
||||
tag: tag,
|
||||
salt: salt,
|
||||
verifier: verifier,
|
||||
}),
|
||||
}
|
||||
).then((res) => {
|
||||
if (res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
return res;
|
||||
console.log("Failed to issue the backup key");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default issueBackupPrivateKey;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -14,9 +15,8 @@ const logout = async (req, res) => {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
credentials: "include"
|
||||
})
|
||||
.then(res => {
|
||||
credentials: "include",
|
||||
}).then((res) => {
|
||||
if (res.status == 200) {
|
||||
SecurityClient.setToken("");
|
||||
// Delete the cookie by not setting a value; Alternatively clear the local storage
|
||||
@@ -30,7 +30,7 @@ const logout = async (req, res) => {
|
||||
} else {
|
||||
console.log("Failed to log out");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default logout;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -6,7 +7,7 @@ import { PATH } from "../../../const";
|
||||
* @param {*} clientPublicKey
|
||||
* @returns
|
||||
*/
|
||||
const SRP1 = ({clientPublicKey}) => {
|
||||
const SRP1 = ({ clientPublicKey }) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/password/srp1", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
@@ -15,14 +16,13 @@ const SRP1 = ({clientPublicKey}) => {
|
||||
body: JSON.stringify({
|
||||
clientPublicKey,
|
||||
}),
|
||||
})
|
||||
.then(async res => {
|
||||
}).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json());
|
||||
return await res.json();
|
||||
} else {
|
||||
console.log('Failed to do the first step of SRP');
|
||||
console.log("Failed to do the first step of SRP");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default SRP1;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient.js";
|
||||
import SecurityClient from "~/utilities/SecurityClient.js";
|
||||
|
||||
import { PATH } from "../../../const.js";
|
||||
|
||||
/**
|
||||
@@ -8,26 +9,28 @@ import { PATH } from "../../../const.js";
|
||||
* @returns
|
||||
*/
|
||||
const getSecrets = async (workspaceId, env) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/secret/" +
|
||||
workspaceId +
|
||||
"?" +
|
||||
new URLSearchParams({
|
||||
environment: env,
|
||||
channel: "web",
|
||||
return SecurityClient.fetchCall(
|
||||
PATH +
|
||||
"/api/v1/secret/" +
|
||||
workspaceId +
|
||||
"?" +
|
||||
new URLSearchParams({
|
||||
environment: env,
|
||||
channel: "web",
|
||||
}),
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
), {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json());
|
||||
return await res.json();
|
||||
} else {
|
||||
console.log('Failed to get project secrets');
|
||||
console.log("Failed to get project secrets");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default getSecrets;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -19,14 +20,13 @@ const uploadSecrets = async ({ workspaceId, secrets, keys, environment }) => {
|
||||
environment,
|
||||
channel: "web",
|
||||
}),
|
||||
})
|
||||
.then(async res => {
|
||||
}).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
console.log('Failed to push secrets');
|
||||
console.log("Failed to push secrets");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default uploadSecrets;
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
const changeHerokuConfigVars = ({ integrationId, key, secrets }) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/integration/" + integrationId + "/sync", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
key,
|
||||
secrets,
|
||||
})
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/integration/" + integrationId + "/sync",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
key,
|
||||
secrets,
|
||||
}),
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
console.log('Failed to sync secrets to Heroku');
|
||||
console.log("Failed to sync secrets to Heroku");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default changeHerokuConfigVars;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -7,19 +8,21 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const deleteIntegration = ({ integrationId }) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/integration/" + integrationId, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/integration/" + integrationId,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).workspace;
|
||||
} else {
|
||||
console.log('Failed to delete an integration');
|
||||
console.log("Failed to delete an integration");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default deleteIntegration;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -7,19 +8,21 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const deleteIntegrationAuth = ({ integrationAuthId }) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/integration-auth/" + integrationAuthId, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/integration-auth/" + integrationAuthId,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
console.log('Failed to delete an integration authorization');
|
||||
console.log("Failed to delete an integration authorization");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default deleteIntegrationAuth;
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
const getIntegrationApps = ({ integrationAuthId }) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/integration-auth/" + integrationAuthId + "/apps", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/integration-auth/" + integrationAuthId + "/apps",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).apps;
|
||||
} else {
|
||||
console.log('Failed to get available apps for an integration');
|
||||
console.log("Failed to get available apps for an integration");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default getIntegrationApps;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
const getIntegrations = () => {
|
||||
@@ -7,14 +8,13 @@ const getIntegrations = () => {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
}).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).integrations;
|
||||
} else {
|
||||
console.log('Failed to get project integrations');
|
||||
console.log("Failed to get project integrations");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default getIntegrations;
|
||||
|
||||
@@ -1,32 +1,35 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
* This route starts the integration after teh default one if gonna set up.
|
||||
* This route starts the integration after teh default one if gonna set up.
|
||||
* @param {*} integrationId
|
||||
* @returns
|
||||
*/
|
||||
const startIntegration = ({ integrationId, appName, environment }) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/integration/" + integrationId, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"update": {
|
||||
app: appName,
|
||||
environment,
|
||||
isActive: true
|
||||
}
|
||||
})
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/integration/" + integrationId,
|
||||
{
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
update: {
|
||||
app: appName,
|
||||
environment,
|
||||
isActive: true,
|
||||
},
|
||||
}),
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
console.log('Failed to start an integration');
|
||||
console.log("Failed to start an integration");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default startIntegration;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -7,24 +8,26 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const AuthorizeIntegration = ({ workspaceId, code, integration }) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/integration-auth/oauth-token", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
workspaceId,
|
||||
code,
|
||||
integration
|
||||
}),
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/integration-auth/oauth-token",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
workspaceId,
|
||||
code,
|
||||
integration,
|
||||
}),
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
console.log('Failed to authorize the integration');
|
||||
console.log("Failed to authorize the integration");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default AuthorizeIntegration;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -7,19 +8,21 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const getWorkspaceAuthorizations = ({ workspaceId }) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/workspace/" + workspaceId + "/authorizations", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/workspace/" + workspaceId + "/authorizations",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).authorizations;
|
||||
} else {
|
||||
console.log('Failed to get project authorizations');
|
||||
console.log("Failed to get project authorizations");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default getWorkspaceAuthorizations;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -7,19 +8,21 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const getWorkspaceIntegrations = ({ workspaceId }) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/workspace/" + workspaceId + "/integrations", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/workspace/" + workspaceId + "/integrations",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).integrations;
|
||||
} else {
|
||||
console.log('Failed to get the project integrations');
|
||||
console.log("Failed to get the project integrations");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default getWorkspaceIntegrations;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -8,19 +9,21 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const getOrganization = (req, res) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/organization/" + req.orgId, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/organization/" + req.orgId,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).organization;
|
||||
} else {
|
||||
console.log('Failed to get org info');
|
||||
console.log("Failed to get org info");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default getOrganization;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -8,19 +9,21 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const getOrganizationProjects = (req, res) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/organization/" + req.orgId + "/workspaces", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/organization/" + req.orgId + "/workspaces",
|
||||
{
|
||||
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 for an org');
|
||||
console.log("Failed to get projects for an org");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default getOrganizationProjects;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -8,19 +9,21 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const getOrganizationSubscriptions = (req, res) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/organization/" + req.orgId + "/subscriptions", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/organization/" + req.orgId + "/subscriptions",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).subscriptions;
|
||||
} else {
|
||||
console.log('Failed to get org subscriptions');
|
||||
console.log("Failed to get org subscriptions");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default getOrganizationSubscriptions;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -8,19 +9,21 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const getOrganizationUserProjects = (req, res) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/organization/" + req.orgId + "/my-workspaces", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/organization/" + req.orgId + "/my-workspaces",
|
||||
{
|
||||
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 of a user in an org');
|
||||
console.log("Failed to get projects of a user in an org");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default getOrganizationUserProjects;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -8,19 +9,21 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const getOrganizationUsers = (req, res) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/organization/" + req.orgId + "/users", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/organization/" + req.orgId + "/users",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).users;
|
||||
} else {
|
||||
console.log('Failed to get org users');
|
||||
console.log("Failed to get org users");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default getOrganizationUsers;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -7,20 +8,22 @@ import { PATH } from "../../../const";
|
||||
* @param {*} res
|
||||
* @returns
|
||||
*/
|
||||
const StripeRedirect = ({orgId}) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/organization/" + orgId + "/customer-portal-session", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
if (res.status == 200) {
|
||||
return window.location.href = (await res.json()).url;
|
||||
} else {
|
||||
console.log('Failed to redirect to Stripe');
|
||||
const StripeRedirect = ({ orgId }) => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/organization/" + orgId + "/customer-portal-session",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
})
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (window.location.href = (await res.json()).url);
|
||||
} else {
|
||||
console.log("Failed to redirect to Stripe");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export default StripeRedirect;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -7,22 +8,24 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const addIncidentContact = (organizationId, email) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/organization/" + organizationId + "/incidentContactOrg", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: email,
|
||||
}),
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/organization/" + organizationId + "/incidentContactOrg",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: email,
|
||||
}),
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
console.log('Failed to add an incident contact');
|
||||
console.log("Failed to add an incident contact");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default addIncidentContact;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -17,14 +18,13 @@ const addUserToOrg = (email, orgId) => {
|
||||
inviteeEmail: email,
|
||||
organizationId: orgId,
|
||||
}),
|
||||
})
|
||||
.then(async res => {
|
||||
}).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
console.log('Failed to add a user to an org');
|
||||
console.log("Failed to add a user to an org");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default addUserToOrg;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -7,22 +8,24 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const deleteIncidentContact = (organizaionId, email) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/organization/" + organizaionId + "/incidentContactOrg", {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: email,
|
||||
}),
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/organization/" + organizaionId + "/incidentContactOrg",
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: email,
|
||||
}),
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
console.log('Failed to delete an incident contact');
|
||||
console.log("Failed to delete an incident contact");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default deleteIncidentContact;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -7,19 +8,21 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const deleteUserFromOrganization = (membershipId) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/membership-org/" + membershipId, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/membership-org/" + membershipId,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
console.log('Failed to delete a user from an org');
|
||||
console.log("Failed to delete a user from an org");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default deleteUserFromOrganization;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -7,19 +8,21 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const getIncidentContacts = (organizationId) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/organization/" + organizationId + "/incidentContactOrg", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/organization/" + organizationId + "/incidentContactOrg",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).incidentContactsOrg;
|
||||
} else {
|
||||
console.log('Failed to get incident contacts');
|
||||
console.log("Failed to get incident contacts");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default getIncidentContacts;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -13,14 +14,13 @@ const getOrganizations = (req, res) => {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
}).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).organizations;
|
||||
} else {
|
||||
console.log('Failed to get orgs of a user');
|
||||
console.log("Failed to get orgs of a user");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default getOrganizations;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -8,22 +9,24 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const renameOrg = (orgId, newOrgName) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/organization/" + orgId + "/name", {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: newOrgName,
|
||||
}),
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/organization/" + orgId + "/name",
|
||||
{
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: newOrgName,
|
||||
}),
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
console.log('Failed to rename an organization');
|
||||
console.log("Failed to rename an organization");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default renameOrg;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -6,29 +7,36 @@ import { PATH } from "../../../const";
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
const addServiceToken = ({name, workspaceId, environment, expiresIn, publicKey, encryptedKey, nonce}) => {
|
||||
const addServiceToken = ({
|
||||
name,
|
||||
workspaceId,
|
||||
environment,
|
||||
expiresIn,
|
||||
publicKey,
|
||||
encryptedKey,
|
||||
nonce,
|
||||
}) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/service-token/", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name,
|
||||
workspaceId,
|
||||
name,
|
||||
workspaceId,
|
||||
environment,
|
||||
expiresIn,
|
||||
publicKey,
|
||||
expiresIn,
|
||||
publicKey,
|
||||
encryptedKey,
|
||||
nonce
|
||||
})
|
||||
})
|
||||
.then(async res => {
|
||||
nonce,
|
||||
}),
|
||||
}).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).token;
|
||||
} else {
|
||||
console.log('Failed to add service tokens');
|
||||
console.log("Failed to add service tokens");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default addServiceToken;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -6,20 +7,22 @@ import { PATH } from "../../../const";
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
const getServiceTokens = ({workspaceId}) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/workspace/" + workspaceId + "/service-tokens", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
const getServiceTokens = ({ workspaceId }) => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/workspace/" + workspaceId + "/service-tokens",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).serviceTokens;
|
||||
} else {
|
||||
console.log('Failed to get service tokens');
|
||||
console.log("Failed to get service tokens");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default getServiceTokens;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -13,14 +14,13 @@ const getUser = (req, res) => {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
}).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).user;
|
||||
} else {
|
||||
console.log('Failed to get user info');
|
||||
console.log("Failed to get user info");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default getUser;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -8,23 +9,26 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const checkUserAction = ({ action }) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/user-action" +
|
||||
"?" +
|
||||
new URLSearchParams({
|
||||
action,
|
||||
}), {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH +
|
||||
"/api/v1/user-action" +
|
||||
"?" +
|
||||
new URLSearchParams({
|
||||
action,
|
||||
}),
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).userAction;
|
||||
} else {
|
||||
console.log('Failed to check a user action');
|
||||
console.log("Failed to check a user action");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default checkUserAction;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -15,14 +16,13 @@ const registerUserAction = ({ action }) => {
|
||||
body: JSON.stringify({
|
||||
action,
|
||||
}),
|
||||
})
|
||||
.then(async res => {
|
||||
}).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
console.log('Failed to register a user action');
|
||||
console.log("Failed to register a user action");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default registerUserAction;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -8,22 +9,24 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const addUserToWorkspace = (email, workspaceId) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/workspace/" + workspaceId + "/invite-signup", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: email,
|
||||
}),
|
||||
})
|
||||
.then(async res => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json());
|
||||
} else {
|
||||
console.log('Failed to add a user to project');
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/workspace/" + workspaceId + "/invite-signup",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: email,
|
||||
}),
|
||||
}
|
||||
})
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return await res.json();
|
||||
} else {
|
||||
console.log("Failed to add a user to project");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export default addUserToWorkspace;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -8,22 +9,24 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const changeUserRoleInWorkspace = (membershipId, role) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/membership/" + membershipId + "/change-role", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
role: role,
|
||||
}),
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/membership/" + membershipId + "/change-role",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
role: role,
|
||||
}),
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
console.log('Failed to change the user role in a project');
|
||||
console.log("Failed to change the user role in a project");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default changeUserRoleInWorkspace;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -16,14 +17,13 @@ const createWorkspace = (workspaceName, organizationId) => {
|
||||
workspaceName: workspaceName,
|
||||
organizationId: organizationId,
|
||||
}),
|
||||
})
|
||||
.then(async res => {
|
||||
}).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).workspace;
|
||||
} else {
|
||||
console.log('Failed to create a project');
|
||||
console.log("Failed to create a project");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default createWorkspace;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -7,19 +8,21 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const deleteUserFromWorkspace = (membershipId) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/membership/" + membershipId, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/membership/" + membershipId,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
console.log('Failed to delete a user from a project');
|
||||
console.log("Failed to delete a user from a project");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default deleteUserFromWorkspace;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -12,14 +13,13 @@ const deleteWorkspace = (workspaceId) => {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
}).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
console.log('Failed to delete a project');
|
||||
console.log("Failed to delete a project");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default deleteWorkspace;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -7,19 +8,23 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const getLatestFileKey = (workspaceId) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/key/" + workspaceId + "/latest", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json());
|
||||
} else {
|
||||
console.log('Failed to get the latest key pairs for a certain project');
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/key/" + workspaceId + "/latest",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
})
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return await res.json();
|
||||
} else {
|
||||
console.log(
|
||||
"Failed to get the latest key pairs for a certain project"
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export default getLatestFileKey;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -8,19 +9,21 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const getWorkspaceInfo = (req, res) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/workspace/" + req.workspaceId, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/workspace/" + req.workspaceId,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).workspace;
|
||||
} else {
|
||||
console.log('Failed to get project info');
|
||||
console.log("Failed to get project info");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default getWorkspaceInfo;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -8,19 +9,23 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const getWorkspaceKeys = (req, res) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/workspace/" + req.workspaceId + "/keys", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/workspace/" + req.workspaceId + "/keys",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).publicKeys;
|
||||
} else {
|
||||
console.log('Failed to get the public keys of everyone in the workspace');
|
||||
console.log(
|
||||
"Failed to get the public keys of everyone in the workspace"
|
||||
);
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default getWorkspaceKeys;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -8,19 +9,21 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const getWorkspaceUsers = (req, res) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/workspace/" + req.workspaceId + "/users", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/workspace/" + req.workspaceId + "/users",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).users;
|
||||
} else {
|
||||
console.log('Failed to get Project Users');
|
||||
console.log("Failed to get Project Users");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default getWorkspaceUsers;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -13,14 +14,13 @@ const getWorkspaces = (req, res) => {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then(async res => {
|
||||
}).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return (await res.json()).workspaces;
|
||||
} else {
|
||||
console.log('Failed to get projects');
|
||||
console.log("Failed to get projects");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default getWorkspaces;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -8,22 +9,24 @@ import { PATH } from "../../../const";
|
||||
* @returns
|
||||
*/
|
||||
const renameWorkspace = (workspaceId, newWorkspaceName) => {
|
||||
return SecurityClient.fetchCall(PATH + "/api/v1/workspace/" + workspaceId + "/name", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: newWorkspaceName,
|
||||
}),
|
||||
})
|
||||
.then(async res => {
|
||||
return SecurityClient.fetchCall(
|
||||
PATH + "/api/v1/workspace/" + workspaceId + "/name",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: newWorkspaceName,
|
||||
}),
|
||||
}
|
||||
).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
console.log('Failed to rename a project');
|
||||
console.log("Failed to rename a project");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default renameWorkspace;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SecurityClient from "../../../components/utilities/SecurityClient";
|
||||
import SecurityClient from "~/utilities/SecurityClient";
|
||||
|
||||
import { PATH } from "../../../const";
|
||||
|
||||
/**
|
||||
@@ -22,14 +23,13 @@ const uploadKeys = (workspaceId, userId, encryptedKey, nonce) => {
|
||||
nonce: nonce,
|
||||
},
|
||||
}),
|
||||
})
|
||||
.then(async res => {
|
||||
}).then(async (res) => {
|
||||
if (res.status == 200) {
|
||||
return res;
|
||||
} else {
|
||||
console.log('Failed to upload keys for a new user');
|
||||
console.log("Failed to upload keys for a new user");
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
export default uploadKeys;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import Head from "next/head";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import getWorkspaces from "./api/workspace/getWorkspaces";
|
||||
|
||||
@@ -10,11 +10,14 @@ export default function DashboardRedirect() {
|
||||
/**
|
||||
* Here we forward to the default workspace if a user opens this url
|
||||
*/
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
useEffect(async () => {
|
||||
let userWorkspace;
|
||||
try {
|
||||
if (localStorage.getItem("projectData.id")) {
|
||||
router.push("/dashboard/" + localStorage.getItem("projectData.id"));
|
||||
router.push(
|
||||
"/dashboard/" + localStorage.getItem("projectData.id")
|
||||
);
|
||||
} else {
|
||||
const userWorkspaces = await getWorkspaces();
|
||||
userWorkspace = userWorkspaces[0]._id;
|
||||
@@ -23,9 +26,10 @@ export default function DashboardRedirect() {
|
||||
} catch (error) {
|
||||
console.log("Error - Not logged in yet");
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return <div></div>
|
||||
return <div></div>;
|
||||
}
|
||||
|
||||
DashboardRedirect.requireAuth = true;
|
||||
|
||||
@@ -1,44 +1,52 @@
|
||||
import React, { useState, useEffect, useCallback, Fragment } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { Fragment, useCallback, useEffect, useState } from "react";
|
||||
import Head from "next/head";
|
||||
import Image from "next/image";
|
||||
import guidGenerator from "../../components/utilities/randomId";
|
||||
import getSecretsForProject from "../../components/utilities/getSecretsForProject";
|
||||
import pushKeys from "../../components/utilities/pushKeys";
|
||||
import getWorkspaces from "../api/workspace/getWorkspaces";
|
||||
import getUser from "../api/user/getUser";
|
||||
import NavHeader from "../../components/navigation/NavHeader";
|
||||
|
||||
import DashboardInputField from "../../components/dashboard/DashboardInputField";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useRouter } from "next/router";
|
||||
import {
|
||||
faMagnifyingGlass,
|
||||
faEye,
|
||||
faEyeSlash,
|
||||
faPlus,
|
||||
faFolderOpen,
|
||||
faArrowDownAZ,
|
||||
faArrowDownZA,
|
||||
faCheck,
|
||||
faCircleInfo,
|
||||
faCopy,
|
||||
faDownload,
|
||||
faEllipsis,
|
||||
faPerson,
|
||||
faEye,
|
||||
faEyeSlash,
|
||||
faFolderOpen,
|
||||
faMagnifyingGlass,
|
||||
faPeopleGroup,
|
||||
faCheck,
|
||||
faCopy,
|
||||
faCircleInfo,
|
||||
faX
|
||||
faPerson,
|
||||
faPlus,
|
||||
faX,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import ListBox from "../../components/basic/Listbox";
|
||||
import DropZone from "../../components/dashboard/DropZone";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Menu, Transition } from "@headlessui/react";
|
||||
|
||||
import Button from "~/components/basic/buttons/Button";
|
||||
import ListBox from "~/components/basic/Listbox";
|
||||
import BottonRightPopup from "~/components/basic/popups/BottomRightPopup";
|
||||
import DashboardInputField from "~/components/dashboard/DashboardInputField";
|
||||
import DropZone from "~/components/dashboard/DropZone";
|
||||
import NavHeader from "~/components/navigation/NavHeader";
|
||||
import getSecretsForProject from "~/utilities/getSecretsForProject";
|
||||
import pushKeys from "~/utilities/pushKeys";
|
||||
import pushKeysIntegration from "~/utilities/pushKeysIntegration";
|
||||
import guidGenerator from "~/utilities/randomId";
|
||||
|
||||
import getWorkspaceIntegrations from "../api/integrations/getWorkspaceIntegrations";
|
||||
import BottonRightPopup from "../../components/basic/popups/BottomRightPopup";
|
||||
import getUser from "../api/user/getUser";
|
||||
import checkUserAction from "../api/userActions/checkUserAction";
|
||||
import registerUserAction from "../api/userActions/registerUserAction";
|
||||
import pushKeysIntegration from "../../components/utilities/pushKeysIntegration";
|
||||
import Button from "../../components/basic/buttons/Button";
|
||||
import getWorkspaces from "../api/workspace/getWorkspaces";
|
||||
|
||||
const KeyPair = ({ keyPair, deleteRow, modifyKey, modifyValue, modifyVisibility, isBlurred }) => {
|
||||
const KeyPair = ({
|
||||
keyPair,
|
||||
deleteRow,
|
||||
modifyKey,
|
||||
modifyValue,
|
||||
modifyVisibility,
|
||||
isBlurred,
|
||||
}) => {
|
||||
return (
|
||||
<div className="px-1 flex flex-col items-center ml-1">
|
||||
<div className="relative flex flex-row justify-between w-full max-w-5xl mr-auto max-h-10 my-1 items-center px-2">
|
||||
@@ -86,9 +94,12 @@ const KeyPair = ({ keyPair, deleteRow, modifyKey, modifyValue, modifyVisibility,
|
||||
<Menu.Items className="absolute right-0 mt-0.5 w-44 origin-top-right rounded-md bg-bunker border border-mineshaft-700 shadow-lg ring-1 ring-black z-20 ring-opacity-5 focus:outline-none px-1 py-1">
|
||||
<div
|
||||
onClick={() =>
|
||||
modifyVisibility(keyPair[4] == "personal"
|
||||
? "shared"
|
||||
: "personal", keyPair[1])
|
||||
modifyVisibility(
|
||||
keyPair[4] == "personal"
|
||||
? "shared"
|
||||
: "personal",
|
||||
keyPair[1]
|
||||
)
|
||||
}
|
||||
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"
|
||||
>
|
||||
@@ -123,7 +134,6 @@ const KeyPair = ({ keyPair, deleteRow, modifyKey, modifyValue, modifyVisibility,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const envMapping = {
|
||||
Development: "dev",
|
||||
Staging: "staging",
|
||||
@@ -186,57 +196,60 @@ export default function Dashboard() {
|
||||
};
|
||||
}, [buttonReady]);
|
||||
|
||||
|
||||
const reorderRows = () => {
|
||||
setSortMethod(
|
||||
sortMethod == "alphabetical" ? "-alphabetical" : "alphabetical"
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(async () => {
|
||||
try {
|
||||
let userWorkspaces = await getWorkspaces();
|
||||
const listWorkspaces = userWorkspaces.map(
|
||||
(workspace) => workspace._id
|
||||
);
|
||||
if (
|
||||
!listWorkspaces.includes(
|
||||
router.asPath.split("/")[2].split("?")[0]
|
||||
)
|
||||
) {
|
||||
router.push("/dashboard/" + listWorkspaces[0]);
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
let userWorkspaces = await getWorkspaces();
|
||||
const listWorkspaces = userWorkspaces.map(
|
||||
(workspace) => workspace._id
|
||||
);
|
||||
if (
|
||||
!listWorkspaces.includes(
|
||||
router.asPath.split("/")[2].split("?")[0]
|
||||
)
|
||||
) {
|
||||
router.push("/dashboard/" + listWorkspaces[0]);
|
||||
}
|
||||
|
||||
if (env != router.asPath.split("?")[1]) {
|
||||
router.push(router.asPath.split("?")[0] + "?" + env);
|
||||
}
|
||||
setBlurred(true);
|
||||
setWorkspaceId(router.query.id);
|
||||
|
||||
await getSecretsForProject({
|
||||
env,
|
||||
setFileState,
|
||||
setIsKeyAvailable,
|
||||
setData,
|
||||
workspaceId: router.query.id,
|
||||
});
|
||||
|
||||
const user = await getUser();
|
||||
setIsNew(
|
||||
(Date.parse(new Date()) - Date.parse(user.createdAt)) /
|
||||
60000 <
|
||||
3
|
||||
? true
|
||||
: false
|
||||
);
|
||||
|
||||
let userAction = await checkUserAction({
|
||||
action: "first_time_secrets_pushed",
|
||||
});
|
||||
setHasUserEverPushed(userAction ? true : false);
|
||||
} catch (error) {
|
||||
console.log("Error", error);
|
||||
setData([]);
|
||||
}
|
||||
|
||||
if (env != router.asPath.split("?")[1]) {
|
||||
router.push(router.asPath.split("?")[0] + "?" + env);
|
||||
}
|
||||
setBlurred(true);
|
||||
setWorkspaceId(router.query.id);
|
||||
|
||||
await getSecretsForProject({
|
||||
env,
|
||||
setFileState,
|
||||
setIsKeyAvailable,
|
||||
setData,
|
||||
workspaceId: router.query.id
|
||||
})
|
||||
|
||||
const user = await getUser();
|
||||
setIsNew(
|
||||
(Date.parse(new Date()) -
|
||||
Date.parse(user.createdAt)) /
|
||||
60000 <
|
||||
3
|
||||
? true
|
||||
: false
|
||||
);
|
||||
|
||||
let userAction = await checkUserAction({action: "first_time_secrets_pushed"});
|
||||
setHasUserEverPushed(userAction ? true : false)
|
||||
} catch (error) {
|
||||
console.log("Error", error);
|
||||
setData([]);
|
||||
}
|
||||
})();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [env]);
|
||||
|
||||
const addRow = () => {
|
||||
@@ -252,39 +265,38 @@ export default function Dashboard() {
|
||||
const modifyValue = (value, id) => {
|
||||
setData((oldData) => {
|
||||
oldData[id][3] = value;
|
||||
return [...oldData]
|
||||
return [...oldData];
|
||||
});
|
||||
setButtonReady(true);
|
||||
}
|
||||
};
|
||||
|
||||
const modifyKey = (value, id) => {
|
||||
setData((oldData) => {
|
||||
oldData[id][2] = value;
|
||||
return [...oldData]
|
||||
return [...oldData];
|
||||
});
|
||||
setButtonReady(true);
|
||||
}
|
||||
};
|
||||
|
||||
const modifyVisibility = (value, id) => {
|
||||
setData((oldData) => {
|
||||
oldData[id][4] = value;
|
||||
return [...oldData]
|
||||
return [...oldData];
|
||||
});
|
||||
setButtonReady(true);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const listenChangeValue = useCallback((value, id) => {
|
||||
modifyValue(value, id)
|
||||
}, [])
|
||||
modifyValue(value, id);
|
||||
}, []);
|
||||
|
||||
const listenChangeKey = useCallback((value, id) => {
|
||||
modifyKey(value, id)
|
||||
}, [])
|
||||
modifyKey(value, id);
|
||||
}, []);
|
||||
|
||||
const listenChangeVisibility = useCallback((value, id) => {
|
||||
modifyVisibility(value, id)
|
||||
}, [])
|
||||
modifyVisibility(value, id);
|
||||
}, []);
|
||||
|
||||
const savePush = async () => {
|
||||
let obj = Object.assign(
|
||||
@@ -294,19 +306,28 @@ export default function Dashboard() {
|
||||
setButtonReady(false);
|
||||
pushKeys(obj, router.query.id, env);
|
||||
|
||||
let integrations = await getWorkspaceIntegrations({workspaceId: router.query.id});
|
||||
let integrations = await getWorkspaceIntegrations({
|
||||
workspaceId: router.query.id,
|
||||
});
|
||||
integrations.map(async (integration) => {
|
||||
if (envMapping[env] == integration.environment && integration.isActive == true) {
|
||||
if (
|
||||
envMapping[env] == integration.environment &&
|
||||
integration.isActive == true
|
||||
) {
|
||||
let objIntegration = Object.assign(
|
||||
{},
|
||||
...data.map((row) => ({ [row[2]]: row[3] }))
|
||||
);
|
||||
await pushKeysIntegration({obj: objIntegration, integrationId: integration._id});}
|
||||
await pushKeysIntegration({
|
||||
obj: objIntegration,
|
||||
integrationId: integration._id,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (!hasUserEverPushed) {
|
||||
setCheckDocsPopUpVisible(true)
|
||||
await registerUserAction({action: "first_time_secrets_pushed"});
|
||||
setCheckDocsPopUpVisible(true);
|
||||
await registerUserAction({ action: "first_time_secrets_pushed" });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -339,20 +360,19 @@ export default function Dashboard() {
|
||||
function copyToClipboard() {
|
||||
// Get the text field
|
||||
var copyText = document.getElementById("myInput");
|
||||
|
||||
|
||||
// Select the text field
|
||||
copyText.select();
|
||||
copyText.setSelectionRange(0, 99999); // For mobile devices
|
||||
|
||||
// Copy the text inside the text field
|
||||
|
||||
// Copy the text inside the text field
|
||||
navigator.clipboard.writeText(copyText.value);
|
||||
|
||||
|
||||
setProjectIdCopied(true);
|
||||
setTimeout(() => setProjectIdCopied(false), 2000);
|
||||
// Alert the copied text
|
||||
// alert("Copied the text: " + copyText.value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return data ? (
|
||||
<div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white">
|
||||
@@ -371,41 +391,61 @@ export default function Dashboard() {
|
||||
</Head>
|
||||
<div className="flex flex-row">
|
||||
<div className="w-full max-h-96 pb-2">
|
||||
<NavHeader pageName="Secrets" isProjectRelated={true}/>
|
||||
{checkDocsPopUpVisible &&
|
||||
<BottonRightPopup
|
||||
buttonText="Check Docs"
|
||||
<NavHeader pageName="Secrets" isProjectRelated={true} />
|
||||
{checkDocsPopUpVisible && (
|
||||
<BottonRightPopup
|
||||
buttonText="Check Docs"
|
||||
buttonLink="https://infisical.com/docs/getting-started/introduction"
|
||||
titleText="Good job!"
|
||||
emoji="🎉"
|
||||
textLine1="Congrats on adding more secrets."
|
||||
textLine2="Here is how to connect them to your codebase."
|
||||
titleText="Good job!"
|
||||
emoji="🎉"
|
||||
textLine1="Congrats on adding more secrets."
|
||||
textLine2="Here is how to connect them to your codebase."
|
||||
setCheckDocsPopUpVisible={setCheckDocsPopUpVisible}
|
||||
/>
|
||||
}
|
||||
)}
|
||||
<div className="flex flex-row justify-between items-center mx-6 mt-6 mb-3 text-xl max-w-5xl">
|
||||
<div className="flex flex-row justify-start items-center text-3xl">
|
||||
<p className="font-semibold mr-4 mt-1">Secrets</p>
|
||||
{data?.length==0 && <ListBox
|
||||
selected={env}
|
||||
data={[
|
||||
"Development",
|
||||
"Staging",
|
||||
"Production",
|
||||
"Testing",
|
||||
]}
|
||||
// ref={useRef(123)}
|
||||
onChange={setEnv}
|
||||
className="z-40"
|
||||
/>}
|
||||
{data?.length == 0 && (
|
||||
<ListBox
|
||||
selected={env}
|
||||
data={[
|
||||
"Development",
|
||||
"Staging",
|
||||
"Production",
|
||||
"Testing",
|
||||
]}
|
||||
// ref={useRef(123)}
|
||||
onChange={setEnv}
|
||||
className="z-40"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-row">
|
||||
<div className="flex justify-end items-center bg-white/[0.07] text-base mt-2 mr-2 rounded-md text-gray-400">
|
||||
<p className="mr-2 font-bold pl-4">Project ID:</p>
|
||||
<input type="text" value={workspaceId} id="myInput" className="bg-white/0 text-gray-400 py-2 w-60 px-2 min-w-md outline-none" disabled></input>
|
||||
<p className="mr-2 font-bold pl-4">
|
||||
Project ID:
|
||||
</p>
|
||||
<input
|
||||
type="text"
|
||||
value={workspaceId}
|
||||
id="myInput"
|
||||
className="bg-white/0 text-gray-400 py-2 w-60 px-2 min-w-md outline-none"
|
||||
disabled
|
||||
></input>
|
||||
<div className="group font-normal group relative inline-block text-gray-400 underline hover:text-primary duration-200">
|
||||
<button onClick={copyToClipboard} className="pl-4 pr-4 border-l border-white/20 py-2 hover:bg-white/[0.12] duration-200">
|
||||
{projectIdCopied ? <FontAwesomeIcon icon={faCheck} className="pr-0.5"/> : <FontAwesomeIcon icon={faCopy} />}
|
||||
<button
|
||||
onClick={copyToClipboard}
|
||||
className="pl-4 pr-4 border-l border-white/20 py-2 hover:bg-white/[0.12] duration-200"
|
||||
>
|
||||
{projectIdCopied ? (
|
||||
<FontAwesomeIcon
|
||||
icon={faCheck}
|
||||
className="pr-0.5"
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon icon={faCopy} />
|
||||
)}
|
||||
</button>
|
||||
<span className="absolute hidden group-hover:flex group-hover:animate-popup duration-300 w-28 -left-8 -top-20 translate-y-full pl-3 py-2 bg-white/10 rounded-md text-center text-gray-400 text-sm">
|
||||
Click to Copy
|
||||
@@ -455,7 +495,9 @@ export default function Dashboard() {
|
||||
className="pl-2 text-gray-400 rounded-r-md bg-white/5 w-full h-full outline-none"
|
||||
value={searchKeys}
|
||||
onChange={(e) =>
|
||||
setSearchKeys(e.target.value)
|
||||
setSearchKeys(
|
||||
e.target.value
|
||||
)
|
||||
}
|
||||
placeholder={"Search keys..."}
|
||||
/>
|
||||
@@ -465,7 +507,11 @@ export default function Dashboard() {
|
||||
onButtonPressed={reorderRows}
|
||||
color="mineshaft"
|
||||
size="icon-md"
|
||||
icon={sortMethod == "alphabetical" ? faArrowDownAZ : faArrowDownZA}
|
||||
icon={
|
||||
sortMethod == "alphabetical"
|
||||
? faArrowDownAZ
|
||||
: faArrowDownZA
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-2 min-w-max flex flex-row items-start justify-start">
|
||||
@@ -481,7 +527,9 @@ export default function Dashboard() {
|
||||
onButtonPressed={changeBlurred}
|
||||
color="mineshaft"
|
||||
size="icon-md"
|
||||
icon={blurred ? faEye : faEyeSlash}
|
||||
icon={
|
||||
blurred ? faEye : faEyeSlash
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="relative ml-2 min-w-max flex flex-row items-start justify-end">
|
||||
@@ -514,14 +562,17 @@ export default function Dashboard() {
|
||||
<div className="rounded-t-md sticky top-0 z-20 bg-bunker flex flex-row pl-4 pr-6 pt-4 pb-2 items-center justify-between text-gray-300 font-bold">
|
||||
{/* <FontAwesomeIcon icon={faAngleDown} /> */}
|
||||
<div className="flex flex-row items-center">
|
||||
<p className="pl-2 text-lg">Personal</p>
|
||||
<p className="pl-2 text-lg">
|
||||
Personal
|
||||
</p>
|
||||
<div className="group font-normal group relative inline-block text-gray-300 underline hover:text-primary duration-200">
|
||||
<FontAwesomeIcon
|
||||
className="ml-3 mt-1 text-lg"
|
||||
icon={faCircleInfo}
|
||||
/>
|
||||
<span className="absolute hidden group-hover:flex group-hover:animate-popdown duration-300 w-44 -left-16 -top-7 translate-y-full px-2 py-2 bg-gray-700 rounded-md text-center text-gray-100 text-sm after:content-[''] after:absolute after:left-1/2 after:bottom-[100%] after:-translate-x-1/2 after:border-8 after:border-x-transparent after:border-t-transparent after:border-b-gray-700">
|
||||
Personal keys are only visible to you
|
||||
Personal keys are only
|
||||
visible to you
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -534,7 +585,8 @@ export default function Dashboard() {
|
||||
.toLowerCase()
|
||||
.includes(
|
||||
searchKeys.toLowerCase()
|
||||
) && keyPair[4] == "personal"
|
||||
) &&
|
||||
keyPair[4] == "personal"
|
||||
)
|
||||
.sort((a, b) =>
|
||||
sortMethod == "alphabetical"
|
||||
@@ -546,9 +598,13 @@ export default function Dashboard() {
|
||||
key={keyPair[0]}
|
||||
keyPair={keyPair}
|
||||
deleteRow={deleteCertainRow}
|
||||
modifyValue={listenChangeValue}
|
||||
modifyValue={
|
||||
listenChangeValue
|
||||
}
|
||||
modifyKey={listenChangeKey}
|
||||
modifyVisibility={listenChangeVisibility}
|
||||
modifyVisibility={
|
||||
listenChangeVisibility
|
||||
}
|
||||
isBlurred={blurred}
|
||||
/>
|
||||
))}
|
||||
@@ -562,15 +618,17 @@ export default function Dashboard() {
|
||||
<div className="sticky top-0 z-10 bg-bunker flex flex-row pl-4 pr-5 pt-4 pb-2 items-center justify-between text-gray-300 font-bold">
|
||||
{/* <FontAwesomeIcon icon={faAngleDown} /> */}
|
||||
<div className="flex flex-row items-center">
|
||||
<p className="pl-2 text-lg">Shared</p>
|
||||
<p className="pl-2 text-lg">
|
||||
Shared
|
||||
</p>
|
||||
<div className="group font-normal group relative inline-block text-gray-300 underline hover:text-primary duration-200">
|
||||
<FontAwesomeIcon
|
||||
className="ml-3 text-lg mt-1"
|
||||
icon={faCircleInfo}
|
||||
/>
|
||||
<span className="absolute hidden group-hover:flex group-hover:animate-popdown duration-300 w-44 -left-16 -top-7 translate-y-full px-2 py-2 bg-gray-700 rounded-md text-center text-gray-100 text-sm after:content-[''] after:absolute after:left-1/2 after:bottom-[100%] after:-translate-x-1/2 after:border-8 after:border-x-transparent after:border-t-transparent after:border-b-gray-700">
|
||||
Shared keys are visible to your whole
|
||||
team
|
||||
Shared keys are visible to
|
||||
your whole team
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -583,7 +641,8 @@ export default function Dashboard() {
|
||||
.toLowerCase()
|
||||
.includes(
|
||||
searchKeys.toLowerCase()
|
||||
) && keyPair[4] == "shared"
|
||||
) &&
|
||||
keyPair[4] == "shared"
|
||||
)
|
||||
.sort((a, b) =>
|
||||
sortMethod == "alphabetical"
|
||||
@@ -595,9 +654,13 @@ export default function Dashboard() {
|
||||
key={keyPair[0]}
|
||||
keyPair={keyPair}
|
||||
deleteRow={deleteCertainRow}
|
||||
modifyValue={listenChangeValue}
|
||||
modifyValue={
|
||||
listenChangeValue
|
||||
}
|
||||
modifyKey={listenChangeKey}
|
||||
modifyVisibility={listenChangeVisibility}
|
||||
modifyVisibility={
|
||||
listenChangeVisibility
|
||||
}
|
||||
isBlurred={blurred}
|
||||
/>
|
||||
))}
|
||||
@@ -606,7 +669,9 @@ export default function Dashboard() {
|
||||
<div className="w-full max-w-5xl">
|
||||
<DropZone
|
||||
setData={addData}
|
||||
setErrorDragAndDrop={setErrorDragAndDrop}
|
||||
setErrorDragAndDrop={
|
||||
setErrorDragAndDrop
|
||||
}
|
||||
createNewFile={addRow}
|
||||
errorDragAndDrop={errorDragAndDrop}
|
||||
setButtonReady={setButtonReady}
|
||||
@@ -617,19 +682,23 @@ export default function Dashboard() {
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center h-full text-xl text-gray-400 max-w-5xl mt-28">
|
||||
{fileState.message != "There's nothing to pull" &&
|
||||
{fileState.message !=
|
||||
"There's nothing to pull" &&
|
||||
fileState.message != undefined && (
|
||||
<FontAwesomeIcon
|
||||
className="text-7xl mb-8"
|
||||
icon={faFolderOpen}
|
||||
/>
|
||||
)}
|
||||
{(fileState.message == "There's nothing to pull" ||
|
||||
{(fileState.message ==
|
||||
"There's nothing to pull" ||
|
||||
fileState.message == undefined) &&
|
||||
isKeyAvailable && (
|
||||
<DropZone
|
||||
setData={setData}
|
||||
setErrorDragAndDrop={setErrorDragAndDrop}
|
||||
setErrorDragAndDrop={
|
||||
setErrorDragAndDrop
|
||||
}
|
||||
createNewFile={addRow}
|
||||
errorDragAndDrop={errorDragAndDrop}
|
||||
setButtonReady={setButtonReady}
|
||||
@@ -638,7 +707,10 @@ export default function Dashboard() {
|
||||
)}
|
||||
{fileState.message ==
|
||||
"Failed membership validation for workspace" && (
|
||||
<p>You are not authorized to view this project.</p>
|
||||
<p>
|
||||
You are not authorized to view this
|
||||
project.
|
||||
</p>
|
||||
)}
|
||||
{fileState.message ==
|
||||
"Access needed to pull the latest file" ||
|
||||
@@ -653,8 +725,8 @@ export default function Dashboard() {
|
||||
administrator for permission.
|
||||
</p>
|
||||
<p className="mt-1">
|
||||
They need to grant you access in the team
|
||||
tab.
|
||||
They need to grant you access in
|
||||
the team tab.
|
||||
</p>
|
||||
</>
|
||||
))}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { useRouter } from "next/router";
|
||||
const queryString = require("query-string");
|
||||
import AuthorizeIntegration from "./api/integrations/authorizeIntegration";
|
||||
|
||||
|
||||
export default function Heroku() {
|
||||
const router = useRouter();
|
||||
const parsedUrl = queryString.parse(router.asPath.split("?")[1]);
|
||||
@@ -14,22 +13,26 @@ export default function Heroku() {
|
||||
/**
|
||||
* Here we forward to the default workspace if a user opens this url
|
||||
*/
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
useEffect(async () => {
|
||||
try {
|
||||
if (state == localStorage.getItem("latestCSRFToken")) {
|
||||
await AuthorizeIntegration({
|
||||
workspaceId: localStorage.getItem("projectData.id"),
|
||||
code,
|
||||
integration: "heroku"
|
||||
})
|
||||
router.push("/integrations/" + localStorage.getItem("projectData.id"));
|
||||
workspaceId: localStorage.getItem("projectData.id"),
|
||||
code,
|
||||
integration: "heroku",
|
||||
});
|
||||
router.push(
|
||||
"/integrations/" + localStorage.getItem("projectData.id")
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Error - Not logged in yet");
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return <div></div>
|
||||
return <div></div>;
|
||||
}
|
||||
|
||||
Heroku.requireAuth = true;
|
||||
|
||||
@@ -4,12 +4,10 @@ import { useRouter } from "next/router";
|
||||
export default function Home() {
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(async () => {
|
||||
useEffect(() => {
|
||||
router.push("/login");
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="bg-bunker-800 w-screen">
|
||||
</div>
|
||||
);
|
||||
return <div className="bg-bunker-800 w-screen"></div>;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,29 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useEffect,useState } from "react";
|
||||
import Head from "next/head";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/router";
|
||||
import {
|
||||
faArrowRight,
|
||||
faCheck,
|
||||
faRotate,
|
||||
faX,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faCheck, faArrowRight, faRotate, faX } from "@fortawesome/free-solid-svg-icons";
|
||||
import ListBox from "../../components/basic/Listbox";
|
||||
import NavHeader from "../../components/navigation/NavHeader";
|
||||
import getIntegrations from "../api/integrations/GetIntegrations";
|
||||
|
||||
import Button from "~/components/basic/buttons/Button";
|
||||
import ListBox from "~/components/basic/Listbox";
|
||||
import NavHeader from "~/components/navigation/NavHeader";
|
||||
import getSecretsForProject from "~/utilities/getSecretsForProject";
|
||||
import pushKeysIntegration from "~/utilities/pushKeysIntegration";
|
||||
import guidGenerator from "~/utilities/randomId";
|
||||
|
||||
import deleteIntegration from "../api/integrations/DeleteIntegration";
|
||||
import deleteIntegrationAuth from "../api/integrations/DeleteIntegrationAuth";
|
||||
import getIntegrationApps from "../api/integrations/GetIntegrationApps";
|
||||
import getIntegrations from "../api/integrations/GetIntegrations";
|
||||
import getWorkspaceAuthorizations from "../api/integrations/getWorkspaceAuthorizations";
|
||||
import getWorkspaceIntegrations from "../api/integrations/getWorkspaceIntegrations";
|
||||
import startIntegration from "../api/integrations/StartIntegration";
|
||||
import deleteIntegration from "../api/integrations/DeleteIntegration";
|
||||
import getSecretsForProject from "../../components/utilities/getSecretsForProject";
|
||||
import pushKeysIntegration from "../../components/utilities/pushKeysIntegration";
|
||||
import deleteIntegrationAuth from "../api/integrations/DeleteIntegrationAuth";
|
||||
import Button from "../../components/basic/buttons/Button";
|
||||
import guidGenerator from "../../components/utilities/randomId";
|
||||
|
||||
const crypto = require("crypto");
|
||||
|
||||
@@ -28,104 +35,138 @@ const envMapping = {
|
||||
};
|
||||
|
||||
const reverseEnvMapping = {
|
||||
"dev": "Development",
|
||||
"staging": "Staging",
|
||||
"prod": "Production",
|
||||
"test": "Testing"
|
||||
}
|
||||
dev: "Development",
|
||||
staging: "Staging",
|
||||
prod: "Production",
|
||||
test: "Testing",
|
||||
};
|
||||
|
||||
const Integration = ({projectIntegration}) => {
|
||||
const [integrationEnvironment, setIntegrationEnvironment] = useState(reverseEnvMapping[projectIntegration.environment]);
|
||||
const Integration = ({ projectIntegration }) => {
|
||||
const [integrationEnvironment, setIntegrationEnvironment] = useState(
|
||||
reverseEnvMapping[projectIntegration.environment]
|
||||
);
|
||||
const [fileState, setFileState] = useState([]);
|
||||
const [data, setData] = useState();
|
||||
const [isKeyAvailable, setIsKeyAvailable] = useState(true);
|
||||
const router = useRouter();
|
||||
const [apps, setApps] = useState([]);
|
||||
const [integrationApp, setIntegrationApp] = useState(projectIntegration.app ? projectIntegration.app : apps[0]);
|
||||
const [integrationApp, setIntegrationApp] = useState(
|
||||
projectIntegration.app ? projectIntegration.app : apps[0]
|
||||
);
|
||||
|
||||
useEffect(async () => {
|
||||
const tempHerokuApps = await getIntegrationApps({integrationAuthId: projectIntegration.integrationAuth});
|
||||
const tempHerokuAppNames = tempHerokuApps.map(app => app.name);
|
||||
const tempHerokuApps = await getIntegrationApps({
|
||||
integrationAuthId: projectIntegration.integrationAuth,
|
||||
});
|
||||
const tempHerokuAppNames = tempHerokuApps.map((app) => app.name);
|
||||
setApps(tempHerokuAppNames);
|
||||
setIntegrationApp(projectIntegration.app ? projectIntegration.app : tempHerokuAppNames[0])
|
||||
setIntegrationApp(
|
||||
projectIntegration.app
|
||||
? projectIntegration.app
|
||||
: tempHerokuAppNames[0]
|
||||
);
|
||||
}, []);
|
||||
|
||||
|
||||
return <div className="flex flex-col max-w-5xl justify-center bg-white/5 p-6 rounded-md mx-6 mt-8">
|
||||
<div className="relative px-4 flex flex-row items-center justify-between mb-4">
|
||||
<div className="flex flex-row">
|
||||
<div>
|
||||
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
|
||||
ENVIRONMENT
|
||||
</div>
|
||||
<ListBox
|
||||
data={!projectIntegration.isActive && ["Development", "Staging", "Production"]}
|
||||
selected={integrationEnvironment}
|
||||
onChange={setIntegrationEnvironment}
|
||||
/>
|
||||
</div>
|
||||
<FontAwesomeIcon
|
||||
icon={faArrowRight}
|
||||
className="mx-4 text-gray-400 mt-8"
|
||||
/>
|
||||
<div className="mr-2">
|
||||
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
|
||||
INTEGRATION
|
||||
</div>
|
||||
<div className="py-2.5 bg-white/[.07] rounded-md pl-4 pr-20 text-sm font-semibold text-gray-300">
|
||||
{projectIntegration.integration.charAt(0).toUpperCase() + projectIntegration.integration.slice(1)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
|
||||
HEROKU APP
|
||||
</div>
|
||||
<ListBox
|
||||
data={!projectIntegration.isActive && apps}
|
||||
selected={integrationApp}
|
||||
onChange={setIntegrationApp}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row mt-6">
|
||||
{projectIntegration.isActive
|
||||
? <div className="max-w-5xl flex flex-row items-center bg-white/5 p-2 rounded-md px-4">
|
||||
<FontAwesomeIcon icon={faRotate} className="text-lg mr-2.5 text-primary animate-spin"/>
|
||||
<div className="text-gray-300 font-semibold">In Sync</div>
|
||||
</div>
|
||||
: <Button
|
||||
text="Start Integration"
|
||||
onButtonPressed={async () => {
|
||||
const result = await startIntegration({
|
||||
"integrationId": projectIntegration._id,
|
||||
"environment": envMapping[integrationEnvironment],
|
||||
"appName": integrationApp
|
||||
})
|
||||
if (result?.status == 200) {
|
||||
let currentSecrets = await getSecretsForProject({
|
||||
env: integrationEnvironment,
|
||||
setFileState,
|
||||
setIsKeyAvailable,
|
||||
setData,
|
||||
workspaceId: router.query.id
|
||||
})
|
||||
|
||||
let obj = Object.assign(
|
||||
{},
|
||||
...currentSecrets.map((row) => ({ [row[2]]: row[3] }))
|
||||
);
|
||||
await pushKeysIntegration({obj, integrationId: projectIntegration._id});
|
||||
router.reload();
|
||||
return (
|
||||
<div className="flex flex-col max-w-5xl justify-center bg-white/5 p-6 rounded-md mx-6 mt-8">
|
||||
<div className="relative px-4 flex flex-row items-center justify-between mb-4">
|
||||
<div className="flex flex-row">
|
||||
<div>
|
||||
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
|
||||
ENVIRONMENT
|
||||
</div>
|
||||
<ListBox
|
||||
data={
|
||||
!projectIntegration.isActive && [
|
||||
"Development",
|
||||
"Staging",
|
||||
"Production",
|
||||
]
|
||||
}
|
||||
}}
|
||||
color="mineshaft"
|
||||
size="md"
|
||||
selected={integrationEnvironment}
|
||||
onChange={setIntegrationEnvironment}
|
||||
/>
|
||||
</div>
|
||||
<FontAwesomeIcon
|
||||
icon={faArrowRight}
|
||||
className="mx-4 text-gray-400 mt-8"
|
||||
/>
|
||||
}
|
||||
<div className="mr-2">
|
||||
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
|
||||
INTEGRATION
|
||||
</div>
|
||||
<div className="py-2.5 bg-white/[.07] rounded-md pl-4 pr-20 text-sm font-semibold text-gray-300">
|
||||
{projectIntegration.integration
|
||||
.charAt(0)
|
||||
.toUpperCase() +
|
||||
projectIntegration.integration.slice(1)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
|
||||
HEROKU APP
|
||||
</div>
|
||||
<ListBox
|
||||
data={!projectIntegration.isActive && apps}
|
||||
selected={integrationApp}
|
||||
onChange={setIntegrationApp}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row mt-6">
|
||||
{projectIntegration.isActive ? (
|
||||
<div className="max-w-5xl flex flex-row items-center bg-white/5 p-2 rounded-md px-4">
|
||||
<FontAwesomeIcon
|
||||
icon={faRotate}
|
||||
className="text-lg mr-2.5 text-primary animate-spin"
|
||||
/>
|
||||
<div className="text-gray-300 font-semibold">
|
||||
In Sync
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<Button
|
||||
text="Start Integration"
|
||||
onButtonPressed={async () => {
|
||||
const result = await startIntegration({
|
||||
integrationId: projectIntegration._id,
|
||||
environment:
|
||||
envMapping[integrationEnvironment],
|
||||
appName: integrationApp,
|
||||
});
|
||||
if (result?.status == 200) {
|
||||
let currentSecrets =
|
||||
await getSecretsForProject({
|
||||
env: integrationEnvironment,
|
||||
setFileState,
|
||||
setIsKeyAvailable,
|
||||
setData,
|
||||
workspaceId: router.query.id,
|
||||
});
|
||||
|
||||
let obj = Object.assign(
|
||||
{},
|
||||
...currentSecrets.map((row) => ({
|
||||
[row[2]]: row[3],
|
||||
}))
|
||||
);
|
||||
await pushKeysIntegration({
|
||||
obj,
|
||||
integrationId: projectIntegration._id,
|
||||
});
|
||||
router.reload();
|
||||
}
|
||||
}}
|
||||
color="mineshaft"
|
||||
size="md"
|
||||
/>
|
||||
)}
|
||||
<div className="opacity-50 hover:opacity-100 duration-200 ml-2">
|
||||
<Button
|
||||
onButtonPressed={async () => {
|
||||
await deleteIntegration({integrationId: projectIntegration._id});
|
||||
await deleteIntegration({
|
||||
integrationId: projectIntegration._id,
|
||||
});
|
||||
router.reload();
|
||||
}}
|
||||
color="red"
|
||||
@@ -134,9 +175,10 @@ const Integration = ({projectIntegration}) => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default function Integrations() {
|
||||
const [integrations, setIntegrations] = useState();
|
||||
@@ -150,12 +192,16 @@ export default function Integrations() {
|
||||
setCsrfToken(tempCSRFToken);
|
||||
localStorage.setItem("latestCSRFToken", tempCSRFToken);
|
||||
|
||||
let projectAuthorizations = await getWorkspaceAuthorizations({workspaceId: router.query.id});
|
||||
let projectAuthorizations = await getWorkspaceAuthorizations({
|
||||
workspaceId: router.query.id,
|
||||
});
|
||||
setAuthorizations(projectAuthorizations);
|
||||
|
||||
const projectIntegrations = await getWorkspaceIntegrations({workspaceId: router.query.id});
|
||||
const projectIntegrations = await getWorkspaceIntegrations({
|
||||
workspaceId: router.query.id,
|
||||
});
|
||||
setProjectIntegrations(projectIntegrations);
|
||||
|
||||
|
||||
try {
|
||||
const integrationsData = await getIntegrations();
|
||||
setIntegrations(integrationsData);
|
||||
@@ -181,7 +227,10 @@ export default function Integrations() {
|
||||
</Head>
|
||||
<div className="flex flex-row">
|
||||
<div className="w-full max-h-96 pb-2 h-screen max-h-[calc(100vh-10px)] overflow-y-scroll no-scrollbar no-scrollbar::-webkit-scrollbar">
|
||||
<NavHeader pageName="Project Integrations" isProjectRelated={true}/>
|
||||
<NavHeader
|
||||
pageName="Project Integrations"
|
||||
isProjectRelated={true}
|
||||
/>
|
||||
<div className="flex flex-col justify-between items-start mx-4 mt-6 mb-4 text-xl max-w-5xl px-2">
|
||||
<div className="flex flex-row justify-start items-center text-3xl">
|
||||
<p className="font-semibold mr-4">
|
||||
@@ -189,24 +238,31 @@ export default function Integrations() {
|
||||
</p>
|
||||
</div>
|
||||
<p className="mr-4 text-base text-gray-400">
|
||||
Manage your integrations of Infisical with third-party services.
|
||||
Manage your integrations of Infisical with
|
||||
third-party services.
|
||||
</p>
|
||||
</div>
|
||||
{projectIntegrations.length > 0
|
||||
? projectIntegrations.map(projectIntegration =>
|
||||
<Integration key={guidGenerator()} projectIntegration={projectIntegration} />
|
||||
)
|
||||
: <div className="flex flex-col max-w-5xl justify-center bg-white/5 p-6 rounded-md mx-6 mt-8">
|
||||
{projectIntegrations.length > 0 ? (
|
||||
projectIntegrations.map((projectIntegration) => (
|
||||
<Integration
|
||||
key={guidGenerator()}
|
||||
projectIntegration={projectIntegration}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div className="flex flex-col max-w-5xl justify-center bg-white/5 p-6 rounded-md mx-6 mt-8">
|
||||
<div className="relative px-4 flex flex-col text-gray-400 items-center justify-center">
|
||||
<div className="mb-1">
|
||||
You don't have any integrations set up yet. When you do, they will appear here.
|
||||
You {"don't"} have any integrations set up
|
||||
yet. When you do, they will appear here.
|
||||
</div>
|
||||
<div className="">
|
||||
To start, click on any of the options below. It takes 5 clicks to set up.
|
||||
To start, click on any of the options below.
|
||||
It takes 5 clicks to set up.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
<div className="flex flex-col justify-between items-start mx-4 mt-12 mb-4 text-xl max-w-5xl px-2">
|
||||
<div className="flex flex-row justify-start items-center text-3xl">
|
||||
<p className="font-semibold mr-4">
|
||||
@@ -214,50 +270,151 @@ export default function Integrations() {
|
||||
</p>
|
||||
</div>
|
||||
<p className="mr-4 text-base text-gray-400">
|
||||
Click on the itegration you want to connect. This will let your environment variables flow automatically into selected third-party services.
|
||||
Click on the itegration you want to connect. This
|
||||
will let your environment variables flow
|
||||
automatically into selected third-party services.
|
||||
</p>
|
||||
<p className="mr-4 text-xs text-gray-600 mt-1">
|
||||
Note: during an integration with Heroku, for security reasons, it is impossible to maintain end-to-end encryption. In theory, this lets Infisical decrypt yor environment variables. In practice, we can assure you that this will never be done, and it allows us to protect your secrets from bad actors online. The core Infisical service will always stay end-to-end encrypted. With any questions, reach out support@infisical.com.
|
||||
Note: during an integration with Heroku, for
|
||||
security reasons, it is impossible to maintain
|
||||
end-to-end encryption. In theory, this lets
|
||||
Infisical decrypt yor environment variables. In
|
||||
practice, we can assure you that this will never be
|
||||
done, and it allows us to protect your secrets from
|
||||
bad actors online. The core Infisical service will
|
||||
always stay end-to-end encrypted. With any
|
||||
questions, reach out support@infisical.com.
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-4 grid-cols-3 grid-rows-3 mx-6 mt-4 max-w-5xl">
|
||||
{Object.keys(integrations).map(integration =>
|
||||
<div className={`relative ${["Heroku"].includes(integrations[integration].name) ? "" : "opacity-50"}`} key={integrations[integration].name} >
|
||||
<a
|
||||
href={`${["Heroku"].includes(integrations[integration].name) ? `https://id.heroku.com/oauth/authorize?client_id=bc132901-935a-4590-b010-f1857efc380d&response_type=code&scope=write-protected&state=${csrfToken}` : "#"}`}
|
||||
{Object.keys(integrations).map((integration) => (
|
||||
<div
|
||||
className={`relative ${
|
||||
["Heroku"].includes(
|
||||
integrations[integration].name
|
||||
)
|
||||
? ""
|
||||
: "opacity-50"
|
||||
}`}
|
||||
key={integrations[integration].name}
|
||||
>
|
||||
<a
|
||||
href={`${
|
||||
["Heroku"].includes(
|
||||
integrations[integration].name
|
||||
)
|
||||
? `https://id.heroku.com/oauth/authorize?client_id=bc132901-935a-4590-b010-f1857efc380d&response_type=code&scope=write-protected&state=${csrfToken}`
|
||||
: "#"
|
||||
}`}
|
||||
rel="noopener"
|
||||
className={`relative flex flex-row bg-white/5 h-32 rounded-md p-4 items-center ${["Heroku"].includes(integrations[integration].name) ? "hover:bg-white/10 duration-200 cursor-pointer" : "cursor-default grayscale"}`}>
|
||||
className={`relative flex flex-row bg-white/5 h-32 rounded-md p-4 items-center ${
|
||||
["Heroku"].includes(
|
||||
integrations[integration].name
|
||||
)
|
||||
? "hover:bg-white/10 duration-200 cursor-pointer"
|
||||
: "cursor-default grayscale"
|
||||
}`}
|
||||
>
|
||||
<Image
|
||||
src={`/images/integrations/${integrations[integration].name}.png`}
|
||||
height={100}
|
||||
width={100}
|
||||
alt="integration logo"
|
||||
></Image>
|
||||
{integrations[integration].name.split(" ").length > 2
|
||||
? <div className="font-semibold text-gray-300 group-hover:text-gray-200 duration-200 text-3xl ml-8 max-w-xs">
|
||||
<div>{integrations[integration].name.split(" ")[0]}</div>
|
||||
<div className="text-base">{integrations[integration].name.split(" ")[1]}{" "}{integrations[integration].name.split(" ")[2]}</div>
|
||||
{integrations[integration].name.split(" ")
|
||||
.length > 2 ? (
|
||||
<div className="font-semibold text-gray-300 group-hover:text-gray-200 duration-200 text-3xl ml-8 max-w-xs">
|
||||
<div>
|
||||
{
|
||||
integrations[
|
||||
integration
|
||||
].name.split(" ")[0]
|
||||
}
|
||||
</div>
|
||||
: <div className="font-semibold text-gray-300 group-hover:text-gray-200 duration-200 text-2xl ml-8 max-w-xs">{integrations[integration].name}</div>
|
||||
}
|
||||
|
||||
<div className="text-base">
|
||||
{
|
||||
integrations[
|
||||
integration
|
||||
].name.split(" ")[1]
|
||||
}{" "}
|
||||
{
|
||||
integrations[
|
||||
integration
|
||||
].name.split(" ")[2]
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="font-semibold text-gray-300 group-hover:text-gray-200 duration-200 text-2xl ml-8 max-w-xs">
|
||||
{integrations[integration].name}
|
||||
</div>
|
||||
)}
|
||||
</a>
|
||||
{["Heroku"].includes(integrations[integration].name) && authorizations.map(authorization => authorization.integration).includes(integrations[integration].name.toLowerCase()) &&
|
||||
{["Heroku"].includes(
|
||||
integrations[integration].name
|
||||
) &&
|
||||
authorizations
|
||||
.map(
|
||||
(authorization) =>
|
||||
authorization.integration
|
||||
)
|
||||
.includes(
|
||||
integrations[
|
||||
integration
|
||||
].name.toLowerCase()
|
||||
) && (
|
||||
<div className="absolute group z-50 top-0 right-0 flex flex-row">
|
||||
<div
|
||||
onClick={() => {
|
||||
deleteIntegrationAuth({
|
||||
integrationAuthId:
|
||||
authorizations
|
||||
.filter(
|
||||
(
|
||||
authorization
|
||||
) =>
|
||||
authorization.integration ==
|
||||
integrations[
|
||||
integration
|
||||
].name.toLowerCase()
|
||||
)
|
||||
.map(
|
||||
(
|
||||
authorization
|
||||
) =>
|
||||
authorization._id
|
||||
)[0],
|
||||
});
|
||||
router.reload();
|
||||
}}
|
||||
className="cursor-pointer w-max bg-red py-0.5 px-2 rounded-b-md text-xs flex flex-row items-center opacity-0 group-hover:opacity-100 duration-200"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faX}
|
||||
className="text-xs mr-2 py-px"
|
||||
/>
|
||||
Revoke
|
||||
</div>
|
||||
<div className="w-max bg-primary py-0.5 px-2 rounded-bl-md rounded-tr-md text-xs flex flex-row items-center text-black opacity-90 group-hover:opacity-100 duration-200">
|
||||
<FontAwesomeIcon
|
||||
icon={faCheck}
|
||||
className="text-xs mr-2"
|
||||
/>
|
||||
Authorized
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!["Heroku"].includes(
|
||||
integrations[integration].name
|
||||
) && (
|
||||
<div className="absolute group z-50 top-0 right-0 flex flex-row">
|
||||
<div onClick={() => {
|
||||
deleteIntegrationAuth({integrationAuthId: authorizations.filter(authorization => authorization.integration == integrations[integration].name.toLowerCase()).map(authorization => authorization._id)[0]});
|
||||
router.reload();
|
||||
}} className="cursor-pointer w-max bg-red py-0.5 px-2 rounded-b-md text-xs flex flex-row items-center opacity-0 group-hover:opacity-100 duration-200"><FontAwesomeIcon icon={faX} className="text-xs mr-2 py-px"/>Revoke</div>
|
||||
<div className="w-max bg-primary py-0.5 px-2 rounded-bl-md rounded-tr-md text-xs flex flex-row items-center text-black opacity-90 group-hover:opacity-100 duration-200"><FontAwesomeIcon icon={faCheck} className="text-xs mr-2"/>Authorized</div>
|
||||
<div className="w-max bg-yellow py-0.5 px-2 rounded-bl-md rounded-tr-md text-xs flex flex-row items-center text-black opacity-90">
|
||||
Coming Soon
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
{!["Heroku"].includes(integrations[integration].name) &&
|
||||
<div className="absolute group z-50 top-0 right-0 flex flex-row">
|
||||
<div className="w-max bg-yellow py-0.5 px-2 rounded-bl-md rounded-tr-md text-xs flex flex-row items-center text-black opacity-90">Coming Soon</div>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import React, { useEffect,useState } from "react";
|
||||
import Head from "next/head";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
import InputField from "../components/basic/InputField";
|
||||
import Error from "../components/basic/Error";
|
||||
import Button from "../components/basic/buttons/Button";
|
||||
import getWorkspaces from "./api/workspace/getWorkspaces";
|
||||
import attemptLogin from "../components/utilities/attemptLogin";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useRouter } from "next/router";
|
||||
import { faWarning } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import Button from "~/components/basic/buttons/Button";
|
||||
import Error from "~/components/basic/Error";
|
||||
import InputField from "~/components/basic/InputField";
|
||||
import attemptLogin from "~/utilities/attemptLogin";
|
||||
|
||||
import getWorkspaces from "./api/workspace/getWorkspaces";
|
||||
|
||||
export default function Login() {
|
||||
const [email, setEmail] = useState("");
|
||||
@@ -36,13 +36,18 @@ export default function Login() {
|
||||
*/
|
||||
const loginCheck = async () => {
|
||||
setIsLoading(true);
|
||||
await attemptLogin(email, password, setErrorLogin, router, false, true).then(
|
||||
() => {
|
||||
setTimeout(function () {
|
||||
setIsLoading(false);
|
||||
}, 2000);
|
||||
}
|
||||
);
|
||||
await attemptLogin(
|
||||
email,
|
||||
password,
|
||||
setErrorLogin,
|
||||
router,
|
||||
false,
|
||||
true
|
||||
).then(() => {
|
||||
setTimeout(function () {
|
||||
setIsLoading(false);
|
||||
}, 2000);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -122,11 +127,17 @@ export default function Login() {
|
||||
<p className="text-gray-400">I may have <Link href="/login"><u className="text-sky-500 cursor-pointer">forgotten my password.</u></Link></p>
|
||||
</div> */}
|
||||
</div>
|
||||
{false &&
|
||||
{false && (
|
||||
<div className="w-full p-2 flex flex-row items-center bg-white/10 text-gray-300 rounded-md max-w-md mx-auto mt-4">
|
||||
<FontAwesomeIcon icon={faWarning} className="ml-2 mr-6 text-6xl"/>
|
||||
We are experiencing minor technical difficulties. We are working on solving it right now. Please come back in a few minutes.
|
||||
</div>}
|
||||
<FontAwesomeIcon
|
||||
icon={faWarning}
|
||||
className="ml-2 mr-6 text-6xl"
|
||||
/>
|
||||
We are experiencing minor technical difficulties. We are
|
||||
working on solving it right now. Please come back in a few
|
||||
minutes.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faFolderOpen } from "@fortawesome/free-regular-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
export default function NoProjects() {
|
||||
return (
|
||||
|
||||
@@ -13,8 +13,8 @@ export default function Activity() {
|
||||
<p className="mt-2 mb-1 text-xl">Your invite has expired. </p>
|
||||
<p className="text-xl">Ask the administrator for a new one.</p>
|
||||
<p className="text-md mt-8 text-gray-600 max-w-sm text-center">
|
||||
Note: If it still doesn't work, please reach out to us at
|
||||
support@infisical.com
|
||||
Note: If it still {"doesn't work"}, please reach out to us
|
||||
at support@infisical.com
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useEffect,useState } from "react";
|
||||
import Head from "next/head";
|
||||
import Plan from "../../../components/billing/Plan";
|
||||
import getOrganizationSubscriptions from "../../api/organization/GetOrgSubscription"
|
||||
import getOrganizationUsers from "../../api/organization/GetOrgUsers"
|
||||
import NavHeader from "../../../components/navigation/NavHeader";
|
||||
import { STRIPE_PRODUCT_PRO, STRIPE_PRODUCT_STARTER } from "../../../components/utilities/config";
|
||||
|
||||
import Plan from "~/components/billing/Plan";
|
||||
import NavHeader from "~/components/navigation/NavHeader";
|
||||
import { STRIPE_PRODUCT_PRO, STRIPE_PRODUCT_STARTER } from "~/utilities/config";
|
||||
|
||||
import getOrganizationSubscriptions from "../../api/organization/GetOrgSubscription";
|
||||
import getOrganizationUsers from "../../api/organization/GetOrgUsers";
|
||||
|
||||
export default function SettingsBilling() {
|
||||
let [currentPlan, setCurrentPlan] = useState("");
|
||||
@@ -46,57 +47,62 @@ export default function SettingsBilling() {
|
||||
];
|
||||
|
||||
useEffect(async () => {
|
||||
const subscriptions = await getOrganizationSubscriptions({orgId: localStorage.getItem("orgData.id")});
|
||||
setCurrentPlan(subscriptions.data[0].plan.product)
|
||||
const orgUsers = await getOrganizationUsers({orgId: localStorage.getItem("orgData.id")})
|
||||
setNumUsers(orgUsers.length)
|
||||
const subscriptions = await getOrganizationSubscriptions({
|
||||
orgId: localStorage.getItem("orgData.id"),
|
||||
});
|
||||
setCurrentPlan(subscriptions.data[0].plan.product);
|
||||
const orgUsers = await getOrganizationUsers({
|
||||
orgId: localStorage.getItem("orgData.id"),
|
||||
});
|
||||
setNumUsers(orgUsers.length);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white">
|
||||
<Head>
|
||||
<title>Settings - Billing</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Head>
|
||||
<div className="flex flex-row">
|
||||
<div className="w-full max-h-screen pb-2 overflow-y-auto">
|
||||
<NavHeader pageName="Usage & Billing"/>
|
||||
<div className="flex flex-row justify-between items-center ml-6 my-8 text-xl max-w-5xl">
|
||||
<div className="flex flex-col justify-start items-start text-3xl">
|
||||
<p className="font-semibold mr-4 text-gray-200">
|
||||
Usage & Billing
|
||||
</p>
|
||||
<p className="font-normal mr-4 text-gray-400 text-base">
|
||||
View and manage your organization's subscription here.
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white">
|
||||
<Head>
|
||||
<title>Settings - Billing</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Head>
|
||||
<div className="flex flex-row">
|
||||
<div className="w-full max-h-screen pb-2 overflow-y-auto">
|
||||
<NavHeader pageName="Usage & Billing" />
|
||||
<div className="flex flex-row justify-between items-center ml-6 my-8 text-xl max-w-5xl">
|
||||
<div className="flex flex-col justify-start items-start text-3xl">
|
||||
<p className="font-semibold mr-4 text-gray-200">
|
||||
Usage & Billing
|
||||
</p>
|
||||
<p className="font-normal mr-4 text-gray-400 text-base">
|
||||
View and manage your {"organization's"}{" "}
|
||||
subscription here.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col ml-6 text-mineshaft-50">
|
||||
<p className="text-xl font-semibold">Subscription</p>
|
||||
<div className="flex flex-row mt-4 overflow-x-auto">
|
||||
{plans.map((plan) => (
|
||||
<Plan
|
||||
key={plan.name}
|
||||
plan={plan}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex flex-col ml-6 text-mineshaft-50">
|
||||
<p className="text-xl font-semibold">Subscription</p>
|
||||
<div className="flex flex-row mt-4 overflow-x-auto">
|
||||
{plans.map((plan) => (
|
||||
<Plan key={plan.name} plan={plan} />
|
||||
))}
|
||||
</div>
|
||||
<p className="text-xl font-bold mt-12">Current Usage</p>
|
||||
<div className="flex flex-row">
|
||||
<div className="mr-4 mt-8 text-gray-300 w-60 pt-6 pb-10 rounded-md bg-white/5 flex justify-center items-center flex flex-col">
|
||||
<p className="text-6xl font-bold">{numUsers}</p>
|
||||
<p className="text-gray-300">
|
||||
{numUsers > 1
|
||||
? "Organization members"
|
||||
: "Organization member"}
|
||||
</p>
|
||||
</div>
|
||||
<p className='text-xl font-bold mt-12'>Current Usage</p>
|
||||
<div className="flex flex-row">
|
||||
<div className="mr-4 mt-8 text-gray-300 w-60 pt-6 pb-10 rounded-md bg-white/5 flex justify-center items-center flex flex-col">
|
||||
<p className="text-6xl font-bold">{numUsers}</p>
|
||||
<p className="text-gray-300">{numUsers > 1 ? "Organization members" : "Organization member"}</p>
|
||||
</div>
|
||||
{/* <div className="mr-4 mt-8 text-gray-300 w-60 pt-6 pb-10 rounded-md bg-white/5 flex justify-center items-center flex flex-col">
|
||||
{/* <div className="mr-4 mt-8 text-gray-300 w-60 pt-6 pb-10 rounded-md bg-white/5 flex justify-center items-center flex flex-col">
|
||||
<p className="text-6xl font-bold">1 </p>
|
||||
<p className="text-gray-300">Organization projects</p>
|
||||
</div> */}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +1,32 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useEffect,useState } from "react";
|
||||
import Head from "next/head";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faMagnifyingGlass, faPlus, faX } from "@fortawesome/free-solid-svg-icons";
|
||||
import { useRouter } from "next/router";
|
||||
import {
|
||||
faMagnifyingGlass,
|
||||
faPlus,
|
||||
faX,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { faCheck } from "@fortawesome/free-solid-svg-icons";
|
||||
import InputField from "../../../components/basic/InputField";
|
||||
import getWorkspaces from "../../api/workspace/getWorkspaces";
|
||||
import AddIncidentContactDialog from "../../../components/basic/dialog/AddIncidentContactDialog";
|
||||
import getIncidentContacts from "../../api/organization/getIncidentContacts";
|
||||
import deleteIncidentContact from "../../api/organization/deleteIncidentContact";
|
||||
import deleteWorkspace from "../../api/workspace/deleteWorkspace";
|
||||
import AddUserDialog from "../../../components/basic/dialog/AddUserDialog";
|
||||
import UserTable from "../../../components/basic/table/UserTable";
|
||||
import getUser from "../../api/user/getUser";
|
||||
import guidGenerator from "../../../components/utilities/randomId";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import Button from "~/components/basic/buttons/Button";
|
||||
import AddIncidentContactDialog from "~/components/basic/dialog/AddIncidentContactDialog";
|
||||
import AddUserDialog from "~/components/basic/dialog/AddUserDialog";
|
||||
import InputField from "~/components/basic/InputField";
|
||||
import UserTable from "~/components/basic/table/UserTable";
|
||||
import NavHeader from "~/components/navigation/NavHeader";
|
||||
import guidGenerator from "~/utilities/randomId";
|
||||
|
||||
import addUserToOrg from "../../api/organization/addUserToOrg";
|
||||
import getOrganizationUsers from "../../api/organization/GetOrgUsers";
|
||||
import renameOrg from "../../api/organization/renameOrg";
|
||||
import deleteIncidentContact from "../../api/organization/deleteIncidentContact";
|
||||
import getIncidentContacts from "../../api/organization/getIncidentContacts";
|
||||
import getOrganization from "../../api/organization/GetOrg";
|
||||
import getOrganizationSubscriptions from "../../api/organization/GetOrgSubscription";
|
||||
import NavHeader from "../../../components/navigation/NavHeader";
|
||||
import Button from "../../../components/basic/buttons/Button";
|
||||
|
||||
import getOrganizationUsers from "../../api/organization/GetOrgUsers";
|
||||
import renameOrg from "../../api/organization/renameOrg";
|
||||
import getUser from "../../api/user/getUser";
|
||||
import deleteWorkspace from "../../api/workspace/deleteWorkspace";
|
||||
import getWorkspaces from "../../api/workspace/getWorkspaces";
|
||||
|
||||
export default function SettingsOrg() {
|
||||
const [buttonReady, setButtonReady] = useState(false);
|
||||
@@ -51,12 +56,12 @@ export default function SettingsOrg() {
|
||||
});
|
||||
let orgData = org;
|
||||
setOrgName(orgData.name);
|
||||
let incidentContactsData = await getIncidentContacts(localStorage.getItem("orgData.id"));
|
||||
let incidentContactsData = await getIncidentContacts(
|
||||
localStorage.getItem("orgData.id")
|
||||
);
|
||||
|
||||
setIncidentContacts(
|
||||
incidentContactsData?.map(
|
||||
(contact) => contact.email
|
||||
)
|
||||
incidentContactsData?.map((contact) => contact.email)
|
||||
);
|
||||
|
||||
const user = await getUser();
|
||||
@@ -82,8 +87,10 @@ export default function SettingsOrg() {
|
||||
publicKey: user.user?.publicKey,
|
||||
}))
|
||||
);
|
||||
const subscriptions = await getOrganizationSubscriptions({orgId: localStorage.getItem("orgData.id")});
|
||||
setCurrentPlan(subscriptions.data[0].plan.product)
|
||||
const subscriptions = await getOrganizationSubscriptions({
|
||||
orgId: localStorage.getItem("orgData.id"),
|
||||
});
|
||||
setCurrentPlan(subscriptions.data[0].plan.product);
|
||||
}, []);
|
||||
|
||||
const modifyOrgName = (newName) => {
|
||||
@@ -127,7 +134,10 @@ export default function SettingsOrg() {
|
||||
setIncidentContacts(
|
||||
incidentContacts.filter((contact) => contact != incidentContact)
|
||||
);
|
||||
deleteIncidentContact(localStorage.getItem("orgData.id"), incidentContact);
|
||||
deleteIncidentContact(
|
||||
localStorage.getItem("orgData.id"),
|
||||
incidentContact
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -160,7 +170,7 @@ export default function SettingsOrg() {
|
||||
</Head>
|
||||
<div className="flex flex-row">
|
||||
<div className="w-full max-h-screen pb-2 overflow-y-auto">
|
||||
<NavHeader pageName="Organization Settings"/>
|
||||
<NavHeader pageName="Organization Settings" />
|
||||
<AddIncidentContactDialog
|
||||
isOpen={isAddIncidentContactOpen}
|
||||
closeModal={closeAddIncidentContactModal}
|
||||
@@ -201,7 +211,9 @@ export default function SettingsOrg() {
|
||||
>
|
||||
<Button
|
||||
text="Save Changes"
|
||||
onButtonPressed={() => submitChanges(orgName)}
|
||||
onButtonPressed={() =>
|
||||
submitChanges(orgName)
|
||||
}
|
||||
color="mineshaft"
|
||||
size="md"
|
||||
active={buttonReady}
|
||||
@@ -289,7 +301,9 @@ export default function SettingsOrg() {
|
||||
<div className="mt-4 mb-2 min-w-max flex flex-row items-end justify-end justify-center">
|
||||
<Button
|
||||
text="Add Contact"
|
||||
onButtonPressed={openAddIncidentContactModal}
|
||||
onButtonPressed={
|
||||
openAddIncidentContactModal
|
||||
}
|
||||
color="mineshaft"
|
||||
size="md"
|
||||
icon={faPlus}
|
||||
@@ -318,7 +332,10 @@ export default function SettingsOrg() {
|
||||
email.includes(searchIncidentContact)
|
||||
)
|
||||
.map((contact) => (
|
||||
<div key={guidGenerator()} className="flex flex-row items-center justify-between max-w-5xl px-4 py-3 hover:bg-white/5 border-t border-gray-600 w-full">
|
||||
<div
|
||||
key={guidGenerator()}
|
||||
className="flex flex-row items-center justify-between max-w-5xl px-4 py-3 hover:bg-white/5 border-t border-gray-600 w-full"
|
||||
>
|
||||
<p className="text-gray-300">
|
||||
{contact}
|
||||
</p>
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useEffect,useState } from "react";
|
||||
import Head from "next/head";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faCheck, faX } from "@fortawesome/free-solid-svg-icons";
|
||||
import InputField from "../../../components/basic/InputField";
|
||||
import getUser from "../../api/user/getUser";
|
||||
import NavHeader from "../../../components/navigation/NavHeader";
|
||||
import passwordCheck from "../../../components/utilities/checks/PasswordCheck";
|
||||
import changePassword from "../../../components/utilities/changePassword";
|
||||
import issueBackupKey from "../../../components/utilities/issueBackupKey";
|
||||
import Button from "../../../components/basic/buttons/Button";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import Button from "~/components/basic/buttons/Button";
|
||||
import InputField from "~/components/basic/InputField";
|
||||
import NavHeader from "~/components/navigation/NavHeader";
|
||||
import changePassword from "~/utilities/changePassword";
|
||||
import passwordCheck from "~/utilities/checks/PasswordCheck";
|
||||
import issueBackupKey from "~/utilities/issueBackupKey";
|
||||
|
||||
import getUser from "../../api/user/getUser";
|
||||
|
||||
export default function PersonalSettings() {
|
||||
const [personalEmail, setPersonalEmail] = useState("");
|
||||
@@ -31,8 +32,6 @@ export default function PersonalSettings() {
|
||||
setPersonalName(user.firstName + " " + user.lastName);
|
||||
}, []);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white">
|
||||
<Head>
|
||||
@@ -41,7 +40,10 @@ export default function PersonalSettings() {
|
||||
</Head>
|
||||
<div className="flex flex-row">
|
||||
<div className="w-full max-h-screen pb-2 overflow-y-auto">
|
||||
<NavHeader pageName="Personal Settings" isProjectRelated={false}/>
|
||||
<NavHeader
|
||||
pageName="Personal Settings"
|
||||
isProjectRelated={false}
|
||||
/>
|
||||
<div className="flex flex-row justify-between items-center ml-6 mt-8 mb-6 text-xl max-w-5xl">
|
||||
<div className="flex flex-col justify-start items-start text-3xl">
|
||||
<p className="font-semibold mr-4 text-gray-200">
|
||||
@@ -137,52 +139,133 @@ export default function PersonalSettings() {
|
||||
type="password"
|
||||
value={newPassword}
|
||||
isRequired
|
||||
error={passwordErrorLength && passwordErrorLowerCase && passwordErrorNumber}
|
||||
error={
|
||||
passwordErrorLength &&
|
||||
passwordErrorLowerCase &&
|
||||
passwordErrorNumber
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{(passwordErrorLength || passwordErrorLowerCase || passwordErrorNumber) ? <div className="w-full mt-3 bg-white/5 px-2 flex flex-col items-start py-2 rounded-md max-w-xl mb-2">
|
||||
<div className={`text-gray-400 text-sm mb-1`}>Password should contain at least:</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
{passwordErrorLength
|
||||
? <FontAwesomeIcon icon={faX} className="text-md text-red mr-2.5"/>
|
||||
: <FontAwesomeIcon icon={faCheck} className="text-md text-primary mr-2"/>}
|
||||
<div className={`${passwordErrorLength ? "text-gray-400" : "text-gray-600"} text-sm`}>14 characters</div>
|
||||
{passwordErrorLength ||
|
||||
passwordErrorLowerCase ||
|
||||
passwordErrorNumber ? (
|
||||
<div className="w-full mt-3 bg-white/5 px-2 flex flex-col items-start py-2 rounded-md max-w-xl mb-2">
|
||||
<div
|
||||
className={`text-gray-400 text-sm mb-1`}
|
||||
>
|
||||
Password should contain at least:
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
{passwordErrorLength ? (
|
||||
<FontAwesomeIcon
|
||||
icon={faX}
|
||||
className="text-md text-red mr-2.5"
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon
|
||||
icon={faCheck}
|
||||
className="text-md text-primary mr-2"
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={`${
|
||||
passwordErrorLength
|
||||
? "text-gray-400"
|
||||
: "text-gray-600"
|
||||
} text-sm`}
|
||||
>
|
||||
14 characters
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
{passwordErrorLowerCase ? (
|
||||
<FontAwesomeIcon
|
||||
icon={faX}
|
||||
className="text-md text-red mr-2.5"
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon
|
||||
icon={faCheck}
|
||||
className="text-md text-primary mr-2"
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={`${
|
||||
passwordErrorLowerCase
|
||||
? "text-gray-400"
|
||||
: "text-gray-600"
|
||||
} text-sm`}
|
||||
>
|
||||
1 lowercase character
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
{passwordErrorNumber ? (
|
||||
<FontAwesomeIcon
|
||||
icon={faX}
|
||||
className="text-md text-red mr-2.5"
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon
|
||||
icon={faCheck}
|
||||
className="text-md text-primary mr-2"
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={`${
|
||||
passwordErrorNumber
|
||||
? "text-gray-400"
|
||||
: "text-gray-600"
|
||||
} text-sm`}
|
||||
>
|
||||
1 number
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
{passwordErrorLowerCase
|
||||
? <FontAwesomeIcon icon={faX} className="text-md text-red mr-2.5"/>
|
||||
: <FontAwesomeIcon icon={faCheck} className="text-md text-primary mr-2"/>}
|
||||
<div className={`${passwordErrorLowerCase ? "text-gray-400" : "text-gray-600"} text-sm`}>1 lowercase character</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
{passwordErrorNumber
|
||||
? <FontAwesomeIcon icon={faX} className="text-md text-red mr-2.5"/>
|
||||
: <FontAwesomeIcon icon={faCheck} className="text-md text-primary mr-2"/>}
|
||||
<div className={`${passwordErrorNumber ? "text-gray-400" : "text-gray-600"} text-sm`}>1 number</div>
|
||||
</div>
|
||||
</div> : <div className="py-2"></div>}
|
||||
) : (
|
||||
<div className="py-2"></div>
|
||||
)}
|
||||
<div className="flex flex-row items-center mt-3 w-52 pr-3">
|
||||
<Button
|
||||
text="Change Password"
|
||||
onButtonPressed={() => {
|
||||
if (!passwordErrorLength && !passwordErrorLowerCase && !passwordErrorNumber) {
|
||||
if (
|
||||
!passwordErrorLength &&
|
||||
!passwordErrorLowerCase &&
|
||||
!passwordErrorNumber
|
||||
) {
|
||||
changePassword(
|
||||
personalEmail,
|
||||
currentPassword,
|
||||
personalEmail,
|
||||
currentPassword,
|
||||
newPassword,
|
||||
setCurrentPasswordError,
|
||||
setPasswordChanged,
|
||||
setCurrentPassword,
|
||||
setNewPassword
|
||||
)
|
||||
);
|
||||
}
|
||||
}}
|
||||
color="mineshaft"
|
||||
size="md"
|
||||
active={(newPassword != "") && (currentPassword != "") && !(passwordErrorLength || passwordErrorLowerCase || passwordErrorNumber)}
|
||||
active={
|
||||
newPassword != "" &&
|
||||
currentPassword != "" &&
|
||||
!(
|
||||
passwordErrorLength ||
|
||||
passwordErrorLowerCase ||
|
||||
passwordErrorNumber
|
||||
)
|
||||
}
|
||||
textDisabled="Change Password"
|
||||
/>
|
||||
<FontAwesomeIcon icon={faCheck} className={`ml-4 text-primary text-3xl ${passwordChanged ? "opacity-100" : "opacity-0"} duration-300`}/>
|
||||
<FontAwesomeIcon
|
||||
icon={faCheck}
|
||||
className={`ml-4 text-primary text-3xl ${
|
||||
passwordChanged
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
} duration-300`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -193,10 +276,14 @@ export default function PersonalSettings() {
|
||||
Emergency Kit
|
||||
</p>
|
||||
<p className="text-sm text-mineshaft-300 min-w-max">
|
||||
Your Emergency Kit contains the information you’ll need to sign in to your Infisical account.
|
||||
Your Emergency Kit contains the
|
||||
information you’ll need to sign in to
|
||||
your Infisical account.
|
||||
</p>
|
||||
<p className="text-sm text-mineshaft-300 mb-5 min-w-max">
|
||||
Only the latest issued Emergency Kit remains valid. To get a new Emergency Kit, verify your password.
|
||||
Only the latest issued Emergency Kit
|
||||
remains valid. To get a new Emergency
|
||||
Kit, verify your password.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -216,11 +303,11 @@ export default function PersonalSettings() {
|
||||
text="Download Emergency Kit"
|
||||
onButtonPressed={() => {
|
||||
issueBackupKey({
|
||||
email: personalEmail,
|
||||
password: backupPassword,
|
||||
personalName,
|
||||
email: personalEmail,
|
||||
password: backupPassword,
|
||||
personalName,
|
||||
setBackupKeyError,
|
||||
setBackupKeyIssued
|
||||
setBackupKeyIssued,
|
||||
});
|
||||
}}
|
||||
color="mineshaft"
|
||||
@@ -228,7 +315,14 @@ export default function PersonalSettings() {
|
||||
active={backupPassword != ""}
|
||||
textDisabled="Download Emergency Kit"
|
||||
/>
|
||||
<FontAwesomeIcon icon={faCheck} className={`ml-4 text-primary text-3xl ${backupKeyIssued ? "opacity-100" : "opacity-0"} duration-300`}/>
|
||||
<FontAwesomeIcon
|
||||
icon={faCheck}
|
||||
className={`ml-4 text-primary text-3xl ${
|
||||
backupKeyIssued
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
} duration-300`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useEffect,useRef, useState } from "react";
|
||||
import Head from "next/head";
|
||||
|
||||
import { useRouter } from "next/router";
|
||||
import { faCheck, faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import InputField from "../../../components/basic/InputField";
|
||||
|
||||
import Button from "~/components/basic/buttons/Button";
|
||||
import AddServiceTokenDialog from "~/components/basic/dialog/AddServiceTokenDialog";
|
||||
import InputField from "~/components/basic/InputField";
|
||||
import ServiceTokenTable from "~/components/basic/table/ServiceTokenTable";
|
||||
import NavHeader from "~/components/navigation/NavHeader";
|
||||
|
||||
import getServiceTokens from "../../api/serviceToken/getServiceTokens";
|
||||
import deleteWorkspace from "../../api/workspace/deleteWorkspace";
|
||||
import getWorkspaces from "../../api/workspace/getWorkspaces";
|
||||
import renameWorkspace from "../../api/workspace/renameWorkspace";
|
||||
import deleteWorkspace from "../../api/workspace/deleteWorkspace";
|
||||
import NavHeader from "../../../components/navigation/NavHeader";
|
||||
import Button from "../../../components/basic/buttons/Button";
|
||||
import ServiceTokenTable from "../../../components/basic/table/ServiceTokenTable";
|
||||
import getServiceTokens from "../../api/serviceToken/getServiceTokens"
|
||||
import AddServiceTokenDialog from "../../../components/basic/dialog/AddServiceTokenDialog";
|
||||
|
||||
export default function SettingsBasic() {
|
||||
const [buttonReady, setButtonReady] = useState(false);
|
||||
@@ -22,7 +23,8 @@ export default function SettingsBasic() {
|
||||
useState("");
|
||||
const [workspaceId, setWorkspaceId] = useState("");
|
||||
const [isAddOpen, setIsAddOpen] = useState(false);
|
||||
let [isAddServiceTokenDialogOpen, setIsAddServiceTokenDialogOpen] = useState(false);
|
||||
let [isAddServiceTokenDialogOpen, setIsAddServiceTokenDialogOpen] =
|
||||
useState(false);
|
||||
|
||||
useEffect(async () => {
|
||||
let userWorkspaces = await getWorkspaces();
|
||||
@@ -31,7 +33,9 @@ export default function SettingsBasic() {
|
||||
setWorkspaceName(userWorkspace.name);
|
||||
}
|
||||
});
|
||||
let tempServiceTokens = await getServiceTokens({workspaceId: router.query.id});
|
||||
let tempServiceTokens = await getServiceTokens({
|
||||
workspaceId: router.query.id,
|
||||
});
|
||||
setServiceTokens(tempServiceTokens);
|
||||
}, []);
|
||||
|
||||
@@ -59,8 +63,8 @@ export default function SettingsBasic() {
|
||||
|
||||
const closeAddServiceTokenModal = () => {
|
||||
setIsAddServiceTokenDialogOpen(false);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* This function deleted a workspace.
|
||||
* It first checks if there is more than one workspace aviable. Otherwise, it doesn't delete
|
||||
@@ -97,7 +101,10 @@ export default function SettingsBasic() {
|
||||
/>
|
||||
<div className="flex flex-row mr-6 max-w-5xl">
|
||||
<div className="w-full max-h-screen pb-2 overflow-y-auto">
|
||||
<NavHeader pageName="Project Settings" isProjectRelated={true}/>
|
||||
<NavHeader
|
||||
pageName="Project Settings"
|
||||
isProjectRelated={true}
|
||||
/>
|
||||
<div className="flex flex-row justify-between items-center ml-6 my-8 text-xl max-w-5xl">
|
||||
<div className="flex flex-col justify-start items-start text-3xl">
|
||||
<p className="font-semibold mr-4 text-gray-200">
|
||||
@@ -133,7 +140,9 @@ export default function SettingsBasic() {
|
||||
>
|
||||
<Button
|
||||
text="Save Changes"
|
||||
onButtonPressed={() => submitChanges(workspaceName)}
|
||||
onButtonPressed={() =>
|
||||
submitChanges(workspaceName)
|
||||
}
|
||||
color="mineshaft"
|
||||
size="md"
|
||||
active={buttonReady}
|
||||
@@ -157,6 +166,7 @@ export default function SettingsBasic() {
|
||||
For more guidance, including code
|
||||
snipets for various languages and
|
||||
frameworks, see{" "}
|
||||
{/* eslint-disable-next-line react/jsx-no-target-blank */}
|
||||
<a
|
||||
href="https://infisical.com/docs/getting-started/introduction"
|
||||
target="_blank"
|
||||
@@ -184,20 +194,30 @@ export default function SettingsBasic() {
|
||||
Service Tokens
|
||||
</p>
|
||||
<p className="text-base text-gray-400 mb-4">
|
||||
Every service token is specific to you, a certain project and a certain environment within this project.
|
||||
Every service token is specific
|
||||
to you, a certain project and a
|
||||
certain environment within this
|
||||
project.
|
||||
</p>
|
||||
</div>
|
||||
<div className="w-48">
|
||||
<Button
|
||||
text="Add New Token"
|
||||
onButtonPressed={() => {setIsAddServiceTokenDialogOpen(true)}}
|
||||
onButtonPressed={() => {
|
||||
setIsAddServiceTokenDialogOpen(
|
||||
true
|
||||
);
|
||||
}}
|
||||
color="mineshaft"
|
||||
icon={faPlus}
|
||||
size="md"
|
||||
/>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ServiceTokenTable data={serviceTokens} workspaceName={workspaceName}/>
|
||||
<ServiceTokenTable
|
||||
data={serviceTokens}
|
||||
workspaceName={workspaceName}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* <div className="bg-white/5 rounded-md px-6 flex flex-col items-start flex flex-col items-start w-full mb-6 mt-4 pb-6 pt-6">
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
import Head from "next/head";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useRouter } from "next/router";
|
||||
import { faCheck, faWarning, faX } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import Aes256Gcm from "~/components/aes-256-gcm";
|
||||
import Button from "~/components/basic/buttons/Button";
|
||||
import Error from "~/components/basic/Error";
|
||||
import InputField from "~/components/basic/InputField";
|
||||
import attemptLogin from "~/utilities/attemptLogin";
|
||||
import passwordCheck from "~/utilities/checks/PasswordCheck";
|
||||
import issueBackupKey from "~/utilities/issueBackupKey";
|
||||
|
||||
import InputField from "../components/basic/InputField";
|
||||
import Error from "../components/basic/Error";
|
||||
import Button from "../components/basic/buttons/Button";
|
||||
import sendVerificationEmail from "./api/auth/SendVerificationEmail";
|
||||
import checkEmailVerificationCode from "./api/auth/CheckEmailVerificationCode";
|
||||
import completeAccountInformationSignup from "./api/auth/CompleteAccountInformationSignup";
|
||||
import Aes256Gcm from "../components/aes-256-gcm";
|
||||
import passwordCheck from "../components/utilities/checks/PasswordCheck";
|
||||
import sendVerificationEmail from "./api/auth/SendVerificationEmail";
|
||||
import getWorkspaces from "./api/workspace/getWorkspaces";
|
||||
import attemptLogin from "../components/utilities/attemptLogin";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faCheck, faX, faWarning } from "@fortawesome/free-solid-svg-icons";
|
||||
import issueBackupKey from "../components/utilities/issueBackupKey";
|
||||
|
||||
const ReactCodeInput = dynamic(import("react-code-input"));
|
||||
const nacl = require("tweetnacl");
|
||||
@@ -73,7 +73,8 @@ export default function SignUp() {
|
||||
const [passwordErrorNumber, setPasswordErrorNumber] = useState(false);
|
||||
const [passwordErrorUpperCase, setPasswordErrorUpperCase] = useState(false);
|
||||
const [passwordErrorLowerCase, setPasswordErrorLowerCase] = useState(false);
|
||||
const [passwordErrorSpecialChar, setPasswordErrorSpecialChar] = useState(false);
|
||||
const [passwordErrorSpecialChar, setPasswordErrorSpecialChar] =
|
||||
useState(false);
|
||||
const [emailError, setEmailError] = useState(false);
|
||||
const [emailErrorMessage, setEmailErrorMessage] = useState("");
|
||||
const [step, setStep] = useState(1);
|
||||
@@ -108,7 +109,7 @@ export default function SignUp() {
|
||||
// Checking if the code matches the email.
|
||||
const response = await checkEmailVerificationCode(email, code);
|
||||
if (response.status == "200" || code == "111222") {
|
||||
setVerificationToken((await response.json()).token)
|
||||
setVerificationToken((await response.json()).token);
|
||||
setStep(3);
|
||||
} else {
|
||||
setCodeError(true);
|
||||
@@ -180,7 +181,14 @@ export default function SignUp() {
|
||||
|
||||
const { ciphertext, iv, tag } = Aes256Gcm.encrypt(
|
||||
PRIVATE_KEY,
|
||||
password.slice(0, 32).padStart(32 + (password.slice(0, 32).length - new Blob([password]).size), "0")
|
||||
password
|
||||
.slice(0, 32)
|
||||
.padStart(
|
||||
32 +
|
||||
(password.slice(0, 32).length -
|
||||
new Blob([password]).size),
|
||||
"0"
|
||||
)
|
||||
);
|
||||
localStorage.setItem("PRIVATE_KEY", PRIVATE_KEY);
|
||||
|
||||
@@ -191,19 +199,21 @@ export default function SignUp() {
|
||||
},
|
||||
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
|
||||
});
|
||||
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") {
|
||||
@@ -243,7 +253,7 @@ export default function SignUp() {
|
||||
const step1 = (
|
||||
<div className="bg-bunker w-full max-w-md mx-auto h-7/12 py-8 md:px-6 mx-1 mb-48 md:mb-16 rounded-xl drop-shadow-xl">
|
||||
<p className="text-4xl font-semibold flex justify-center text-transparent bg-clip-text bg-gradient-to-br from-sky-400 to-primary">
|
||||
Let's get started
|
||||
{"Let'"}s get started
|
||||
</p>
|
||||
<div className="flex flex-col items-center justify-center w-full md:pb-2 max-h-24 max-w-md mx-auto pt-2">
|
||||
<Link href="/login">
|
||||
@@ -276,7 +286,11 @@ export default function SignUp() {
|
||||
and acknowledged the Privacy Policy.
|
||||
</p>
|
||||
<div className="text-l mt-6 m-2 md:m-8 px-8 py-1 text-lg">
|
||||
<Button text="Get Started" onButtonPressed={emailCheck} size="lg"/>
|
||||
<Button
|
||||
text="Get Started"
|
||||
onButtonPressed={emailCheck}
|
||||
size="lg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -286,7 +300,7 @@ export default function SignUp() {
|
||||
const step2 = (
|
||||
<div className="bg-bunker w-max mx-auto h-7/12 pt-10 pb-4 px-8 rounded-xl drop-shadow-xl mb-64 md:mb-16">
|
||||
<p className="text-l flex justify-center text-gray-400">
|
||||
We've sent a verification email to{" "}
|
||||
{"We've"} sent a verification email to{" "}
|
||||
</p>
|
||||
<p className="text-l flex justify-center font-semibold my-2 text-gray-400">
|
||||
{email}{" "}
|
||||
@@ -313,7 +327,11 @@ export default function SignUp() {
|
||||
<Error text="Oops. Your code is wrong. Please try again." />
|
||||
)}
|
||||
<div className="flex max-w-min flex-col items-center justify-center md:p-2 max-h-24 max-w-md mx-auto text-lg px-4 mt-4 mb-2">
|
||||
<Button text="Verify" onButtonPressed={incrementStep} size="lg"/>
|
||||
<Button
|
||||
text="Verify"
|
||||
onButtonPressed={incrementStep}
|
||||
size="lg"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col items-center justify-center w-full max-h-24 max-w-md mx-auto pt-2">
|
||||
{/* <Link href="/login">
|
||||
@@ -374,37 +392,101 @@ export default function SignUp() {
|
||||
type="password"
|
||||
value={password}
|
||||
isRequired
|
||||
error={passwordErrorLength && passwordErrorNumber && passwordErrorLowerCase}
|
||||
error={
|
||||
passwordErrorLength &&
|
||||
passwordErrorNumber &&
|
||||
passwordErrorLowerCase
|
||||
}
|
||||
/>
|
||||
{(passwordErrorLength || passwordErrorLowerCase || passwordErrorNumber) ? <div className="w-full mt-4 bg-white/5 px-2 flex flex-col items-start py-2 rounded-md">
|
||||
<div className={`text-gray-400 text-sm mb-1`}>Password should contain at least:</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
{passwordErrorLength
|
||||
? <FontAwesomeIcon icon={faX} className="text-md text-red mr-2.5"/>
|
||||
: <FontAwesomeIcon icon={faCheck} className="text-md text-primary mr-2"/>}
|
||||
<div className={`${passwordErrorLength ? "text-gray-400" : "text-gray-600"} text-sm`}>14 characters</div>
|
||||
{passwordErrorLength ||
|
||||
passwordErrorLowerCase ||
|
||||
passwordErrorNumber ? (
|
||||
<div className="w-full mt-4 bg-white/5 px-2 flex flex-col items-start py-2 rounded-md">
|
||||
<div className={`text-gray-400 text-sm mb-1`}>
|
||||
Password should contain at least:
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
{passwordErrorLength ? (
|
||||
<FontAwesomeIcon
|
||||
icon={faX}
|
||||
className="text-md text-red mr-2.5"
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon
|
||||
icon={faCheck}
|
||||
className="text-md text-primary mr-2"
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={`${
|
||||
passwordErrorLength
|
||||
? "text-gray-400"
|
||||
: "text-gray-600"
|
||||
} text-sm`}
|
||||
>
|
||||
14 characters
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
{passwordErrorLowerCase ? (
|
||||
<FontAwesomeIcon
|
||||
icon={faX}
|
||||
className="text-md text-red mr-2.5"
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon
|
||||
icon={faCheck}
|
||||
className="text-md text-primary mr-2"
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={`${
|
||||
passwordErrorLowerCase
|
||||
? "text-gray-400"
|
||||
: "text-gray-600"
|
||||
} text-sm`}
|
||||
>
|
||||
1 lowercase character
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
{passwordErrorNumber ? (
|
||||
<FontAwesomeIcon
|
||||
icon={faX}
|
||||
className="text-md text-red mr-2.5"
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon
|
||||
icon={faCheck}
|
||||
className="text-md text-primary mr-2"
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={`${
|
||||
passwordErrorNumber
|
||||
? "text-gray-400"
|
||||
: "text-gray-600"
|
||||
} text-sm`}
|
||||
>
|
||||
1 number
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
{passwordErrorLowerCase
|
||||
? <FontAwesomeIcon icon={faX} className="text-md text-red mr-2.5"/>
|
||||
: <FontAwesomeIcon icon={faCheck} className="text-md text-primary mr-2"/>}
|
||||
<div className={`${passwordErrorLowerCase ? "text-gray-400" : "text-gray-600"} text-sm`}>1 lowercase character</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
{passwordErrorNumber
|
||||
? <FontAwesomeIcon icon={faX} className="text-md text-red mr-2.5"/>
|
||||
: <FontAwesomeIcon icon={faCheck} className="text-md text-primary mr-2"/>}
|
||||
<div className={`${passwordErrorNumber ? "text-gray-400" : "text-gray-600"} text-sm`}>1 number</div>
|
||||
</div>
|
||||
</div> : <div className="py-2"></div>}
|
||||
) : (
|
||||
<div className="py-2"></div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col items-center justify-center md:p-2 max-h-48 max-w-max mx-auto text-lg px-2 py-3">
|
||||
<Button text="Sign Up" loading={isLoading} onButtonPressed={signupErrorCheck} size="lg" />
|
||||
<Button
|
||||
text="Sign Up"
|
||||
loading={isLoading}
|
||||
onButtonPressed={signupErrorCheck}
|
||||
size="lg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
// Step 4 of the sign up process (download the emergency kit pdf)
|
||||
const step4 = (
|
||||
<div className="bg-bunker flex flex-col items-center w-full max-w-xs md:max-w-lg mx-auto h-7/12 py-8 px-4 md:px-6 mx-1 mb-36 md:mb-16 rounded-xl drop-shadow-xl">
|
||||
@@ -412,23 +494,32 @@ export default function SignUp() {
|
||||
Save your Emergency Kit
|
||||
</p>
|
||||
<div className="flex flex-col items-center justify-center w-full mt-4 md:mt-8 max-w-md text-gray-400 text-md rounded-md px-2">
|
||||
<div>If you get locked out of your account, your Emergency Kit is the only way to sign in.</div>
|
||||
<div className="mt-3">We recommend you download it and keep it somewhere safe.</div>
|
||||
<div>
|
||||
If you get locked out of your account, your Emergency Kit is
|
||||
the only way to sign in.
|
||||
</div>
|
||||
<div className="mt-3">
|
||||
We recommend you download it and keep it somewhere safe.
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full p-2 flex flex-row items-center bg-white/10 text-gray-400 rounded-md max-w-xs md:max-w-md mx-auto mt-4">
|
||||
<FontAwesomeIcon icon={faWarning} className="ml-2 mr-4 text-4xl"/>
|
||||
It contains your Secret Key which we cannot access or recover for you if you lose it.
|
||||
<FontAwesomeIcon
|
||||
icon={faWarning}
|
||||
className="ml-2 mr-4 text-4xl"
|
||||
/>
|
||||
It contains your Secret Key which we cannot access or recover
|
||||
for you if you lose it.
|
||||
</div>
|
||||
<div className="flex flex-row items-center justify-center w-3/4 md:w-full md:p-2 max-h-28 max-w-max mx-auto mt-6 py-1 md:mt-4 text-lg text-center md:text-left">
|
||||
<Button
|
||||
text="Download PDF"
|
||||
<Button
|
||||
text="Download PDF"
|
||||
onButtonPressed={async () => {
|
||||
await issueBackupKey({
|
||||
email,
|
||||
password,
|
||||
personalName: (firstName + " " + lastName),
|
||||
email,
|
||||
password,
|
||||
personalName: firstName + " " + lastName,
|
||||
setBackupKeyError,
|
||||
setBackupKeyIssued
|
||||
setBackupKeyIssued,
|
||||
});
|
||||
router.push("/dashboard/");
|
||||
}}
|
||||
@@ -476,7 +567,13 @@ export default function SignUp() {
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
{step == 1 ? step1 : step == 2 ? step2 : step == 3 ? step3 : step4}
|
||||
{step == 1
|
||||
? step1
|
||||
: step == 2
|
||||
? step2
|
||||
: step == 3
|
||||
? step3
|
||||
: step4}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import React, { useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import Head from "next/head";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
import InputField from "../components/basic/InputField";
|
||||
import Button from "../components/basic/buttons/Button";
|
||||
import completeAccountInformationSignupInvite from "./api/auth/CompleteAccountInformationSignupInvite";
|
||||
import Aes256Gcm from "../components/aes-256-gcm";
|
||||
import passwordCheck from "../components/utilities/checks/PasswordCheck";
|
||||
import attemptLogin from "../components/utilities/attemptLogin";
|
||||
import { useRouter } from "next/router";
|
||||
import { faCheck, faWarning,faX } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faCheck, faX, faWarning } from "@fortawesome/free-solid-svg-icons";
|
||||
import issueBackupKey from "../components/utilities/issueBackupKey";
|
||||
|
||||
import Aes256Gcm from "~/components/aes-256-gcm";
|
||||
import Button from "~/components/basic/buttons/Button";
|
||||
import InputField from "~/components/basic/InputField";
|
||||
import attemptLogin from "~/utilities/attemptLogin";
|
||||
import passwordCheck from "~/utilities/checks/PasswordCheck";
|
||||
import issueBackupKey from "~/utilities/issueBackupKey";
|
||||
|
||||
import completeAccountInformationSignupInvite from "./api/auth/CompleteAccountInformationSignupInvite";
|
||||
import verifySignupInvite from "./api/auth/VerifySignupInvite";
|
||||
|
||||
const nacl = require("tweetnacl");
|
||||
@@ -76,7 +76,14 @@ export default function SignupInvite() {
|
||||
|
||||
const { ciphertext, iv, tag } = Aes256Gcm.encrypt(
|
||||
PRIVATE_KEY,
|
||||
password.slice(0, 32).padStart(32 + (password.slice(0, 32).length - new Blob([password]).size), "0")
|
||||
password
|
||||
.slice(0, 32)
|
||||
.padStart(
|
||||
32 +
|
||||
(password.slice(0, 32).length -
|
||||
new Blob([password]).size),
|
||||
"0"
|
||||
)
|
||||
);
|
||||
localStorage.setItem("PRIVATE_KEY", PRIVATE_KEY);
|
||||
|
||||
@@ -87,18 +94,19 @@ export default function SignupInvite() {
|
||||
},
|
||||
async () => {
|
||||
client.createVerifier(async (err, result) => {
|
||||
const response = await completeAccountInformationSignupInvite({
|
||||
email,
|
||||
firstName,
|
||||
lastName,
|
||||
publicKey: PUBLIC_KEY,
|
||||
ciphertext,
|
||||
iv,
|
||||
tag,
|
||||
salt: result.salt,
|
||||
verifier: result.verifier,
|
||||
token: verificationToken
|
||||
});
|
||||
const response =
|
||||
await completeAccountInformationSignupInvite({
|
||||
email,
|
||||
firstName,
|
||||
lastName,
|
||||
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") {
|
||||
@@ -148,19 +156,19 @@ export default function SignupInvite() {
|
||||
alt="verify email"
|
||||
></Image>
|
||||
<div className="flex flex-row items-center justify-center w-3/4 md:w-full md:p-2 max-h-28 max-w-xs md:max-w-md mx-auto text-lg py-1 text-center md:text-left">
|
||||
<Button
|
||||
<Button
|
||||
text="Confirm Email"
|
||||
onButtonPressed={async () => {
|
||||
const response = await verifySignupInvite({
|
||||
email,
|
||||
code: token
|
||||
email,
|
||||
code: token,
|
||||
});
|
||||
if (response.status == 200) {
|
||||
setVerificationToken((await response.json()).token)
|
||||
setVerificationToken((await response.json()).token);
|
||||
setStep(2);
|
||||
} else {
|
||||
console.log("ERROR", response)
|
||||
router.push("/requestnewinvite")
|
||||
console.log("ERROR", response);
|
||||
router.push("/requestnewinvite");
|
||||
}
|
||||
}}
|
||||
size="lg"
|
||||
@@ -213,29 +221,89 @@ export default function SignupInvite() {
|
||||
type="password"
|
||||
value={password}
|
||||
isRequired
|
||||
error={passwordErrorLength && passwordErrorNumber && passwordErrorLowerCase}
|
||||
error={
|
||||
passwordErrorLength &&
|
||||
passwordErrorNumber &&
|
||||
passwordErrorLowerCase
|
||||
}
|
||||
/>
|
||||
{(passwordErrorLength || passwordErrorLowerCase || passwordErrorNumber) ? <div className="w-full mt-4 bg-white/5 px-2 flex flex-col items-start py-2 rounded-md">
|
||||
<div className={`text-gray-400 text-sm mb-1`}>Password should contain at least:</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
{passwordErrorLength
|
||||
? <FontAwesomeIcon icon={faX} className="text-md text-red mr-2.5"/>
|
||||
: <FontAwesomeIcon icon={faCheck} className="text-md text-primary mr-2"/>}
|
||||
<div className={`${passwordErrorLength ? "text-gray-400" : "text-gray-600"} text-sm`}>14 characters</div>
|
||||
{passwordErrorLength ||
|
||||
passwordErrorLowerCase ||
|
||||
passwordErrorNumber ? (
|
||||
<div className="w-full mt-4 bg-white/5 px-2 flex flex-col items-start py-2 rounded-md">
|
||||
<div className={`text-gray-400 text-sm mb-1`}>
|
||||
Password should contain at least:
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
{passwordErrorLength ? (
|
||||
<FontAwesomeIcon
|
||||
icon={faX}
|
||||
className="text-md text-red mr-2.5"
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon
|
||||
icon={faCheck}
|
||||
className="text-md text-primary mr-2"
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={`${
|
||||
passwordErrorLength
|
||||
? "text-gray-400"
|
||||
: "text-gray-600"
|
||||
} text-sm`}
|
||||
>
|
||||
14 characters
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
{passwordErrorLowerCase ? (
|
||||
<FontAwesomeIcon
|
||||
icon={faX}
|
||||
className="text-md text-red mr-2.5"
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon
|
||||
icon={faCheck}
|
||||
className="text-md text-primary mr-2"
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={`${
|
||||
passwordErrorLowerCase
|
||||
? "text-gray-400"
|
||||
: "text-gray-600"
|
||||
} text-sm`}
|
||||
>
|
||||
1 lowercase character
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
{passwordErrorNumber ? (
|
||||
<FontAwesomeIcon
|
||||
icon={faX}
|
||||
className="text-md text-red mr-2.5"
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon
|
||||
icon={faCheck}
|
||||
className="text-md text-primary mr-2"
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={`${
|
||||
passwordErrorNumber
|
||||
? "text-gray-400"
|
||||
: "text-gray-600"
|
||||
} text-sm`}
|
||||
>
|
||||
1 number
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
{passwordErrorLowerCase
|
||||
? <FontAwesomeIcon icon={faX} className="text-md text-red mr-2.5"/>
|
||||
: <FontAwesomeIcon icon={faCheck} className="text-md text-primary mr-2"/>}
|
||||
<div className={`${passwordErrorLowerCase ? "text-gray-400" : "text-gray-600"} text-sm`}>1 lowercase character</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-start items-center ml-1">
|
||||
{passwordErrorNumber
|
||||
? <FontAwesomeIcon icon={faX} className="text-md text-red mr-2.5"/>
|
||||
: <FontAwesomeIcon icon={faCheck} className="text-md text-primary mr-2"/>}
|
||||
<div className={`${passwordErrorNumber ? "text-gray-400" : "text-gray-600"} text-sm`}>1 number</div>
|
||||
</div>
|
||||
</div> : <div className="py-2"></div>}
|
||||
) : (
|
||||
<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">
|
||||
<Button
|
||||
@@ -257,23 +325,32 @@ export default function SignupInvite() {
|
||||
Save your Emergency Kit
|
||||
</p>
|
||||
<div className="flex flex-col items-center justify-center w-full mt-4 md:mt-8 max-w-md text-gray-400 text-md rounded-md px-2">
|
||||
<div>If you get locked out of your account, your Emergency Kit is the only way to sign in.</div>
|
||||
<div className="mt-3">We recommend you download it and keep it somewhere safe.</div>
|
||||
<div>
|
||||
If you get locked out of your account, your Emergency Kit is
|
||||
the only way to sign in.
|
||||
</div>
|
||||
<div className="mt-3">
|
||||
We recommend you download it and keep it somewhere safe.
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full p-2 flex flex-row items-center bg-white/10 text-gray-400 rounded-md max-w-xs md:max-w-md mx-auto mt-4">
|
||||
<FontAwesomeIcon icon={faWarning} className="ml-2 mr-4 text-4xl"/>
|
||||
It contains your Secret Key which we cannot access or recover for you if you lose it.
|
||||
<FontAwesomeIcon
|
||||
icon={faWarning}
|
||||
className="ml-2 mr-4 text-4xl"
|
||||
/>
|
||||
It contains your Secret Key which we cannot access or recover
|
||||
for you if you lose it.
|
||||
</div>
|
||||
<div className="flex flex-row items-center justify-center w-3/4 md:w-full md:p-2 max-h-28 max-w-xs md:max-w-md mx-auto mt-6 md:mt-8 py-1 text-lg text-center md:text-left">
|
||||
<Button
|
||||
text="Download PDF"
|
||||
<Button
|
||||
text="Download PDF"
|
||||
onButtonPressed={async () => {
|
||||
await issueBackupKey({
|
||||
email,
|
||||
password,
|
||||
personalName: (firstName + " " + lastName),
|
||||
email,
|
||||
password,
|
||||
personalName: firstName + " " + lastName,
|
||||
setBackupKeyError,
|
||||
setBackupKeyIssued
|
||||
setBackupKeyIssued,
|
||||
});
|
||||
router.push("/dashboard/");
|
||||
}}
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Head from "next/head";
|
||||
import Image from "next/image";
|
||||
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useRouter } from "next/router";
|
||||
import { faMagnifyingGlass, faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import UserTable from "../../components/basic/table/UserTable";
|
||||
import getWorkspaceUsers from "../api/workspace/getWorkspaceUsers";
|
||||
import guidGenerator from "../../components/utilities/randomId";
|
||||
import AddProjectMemberDialog from "../../components/basic/dialog/AddProjectMemberDialog";
|
||||
// import DeleteUserDialog from '../../components/basic/dialog/DeleteUserDialog';
|
||||
import addUserToWorkspace from "../api/workspace/addUserToWorkspace";
|
||||
import getUser from "../api/user/getUser";
|
||||
import uploadKeys from "../api/workspace/uploadKeys";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import Button from "~/components/basic/buttons/Button";
|
||||
import AddProjectMemberDialog from "~/components/basic/dialog/AddProjectMemberDialog";
|
||||
import UserTable from "~/components/basic/table/UserTable";
|
||||
import NavHeader from "~/components/navigation/NavHeader";
|
||||
import guidGenerator from "~/utilities/randomId";
|
||||
|
||||
import getOrganizationUsers from "../api/organization/GetOrgUsers";
|
||||
import NavHeader from "../../components/navigation/NavHeader";
|
||||
import Button from "../../components/basic/buttons/Button";
|
||||
import getUser from "../api/user/getUser";
|
||||
// import DeleteUserDialog from '~/components/basic/dialog/DeleteUserDialog';
|
||||
import addUserToWorkspace from "../api/workspace/addUserToWorkspace";
|
||||
import getWorkspaceUsers from "../api/workspace/getWorkspaceUsers";
|
||||
import uploadKeys from "../api/workspace/uploadKeys";
|
||||
|
||||
// #TODO: Update all the workspaceIds
|
||||
const crypto = require("crypto");
|
||||
@@ -85,6 +86,7 @@ export default function Users() {
|
||||
|
||||
const [userList, setUserList] = useState();
|
||||
const [orgUserList, setOrgUserList] = useState([]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
useEffect(async () => {
|
||||
const user = await getUser();
|
||||
setPersonalEmail(user.email);
|
||||
@@ -98,15 +100,13 @@ export default function Users() {
|
||||
firstName: user.user?.firstName,
|
||||
lastName: user.user?.lastName,
|
||||
email:
|
||||
user.user?.email == null
|
||||
? user.inviteEmail
|
||||
: user.user?.email,
|
||||
user.user?.email == null ? user.inviteEmail : user.user?.email,
|
||||
role: user?.role,
|
||||
status: user?.status,
|
||||
userId: user.user?._id,
|
||||
membershipId: user._id,
|
||||
publicKey: user.user?.publicKey,
|
||||
}))
|
||||
}));
|
||||
setUserList(tempUserList);
|
||||
const orgUsers = await getOrganizationUsers({
|
||||
orgId: localStorage.getItem("orgData.id"),
|
||||
@@ -114,15 +114,15 @@ export default function Users() {
|
||||
setOrgUserList(orgUsers);
|
||||
setEmail(
|
||||
orgUsers
|
||||
?.filter((user) => user.status == "accepted")
|
||||
.map((user) => user.user.email)
|
||||
.filter(
|
||||
(email) =>
|
||||
!tempUserList
|
||||
?.map((user1) => user1.email)
|
||||
.includes(email)
|
||||
)[0]
|
||||
)
|
||||
?.filter((user) => user.status == "accepted")
|
||||
.map((user) => user.user.email)
|
||||
.filter(
|
||||
(email) =>
|
||||
!tempUserList
|
||||
?.map((user1) => user1.email)
|
||||
.includes(email)
|
||||
)[0]
|
||||
);
|
||||
}, []);
|
||||
|
||||
return userList ? (
|
||||
@@ -131,7 +131,7 @@ export default function Users() {
|
||||
<title>Users</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Head>
|
||||
<NavHeader pageName="Project Members" isProjectRelated={true}/>
|
||||
<NavHeader pageName="Project Members" isProjectRelated={true} />
|
||||
<div className="flex flex-col justify-start items-start px-6 py-6 pb-4 text-3xl">
|
||||
<p className="font-semibold mr-4 text-white">Project Members</p>
|
||||
<p className="mr-4 text-base text-gray-400">
|
||||
|
||||
5544
frontend/yarn.lock
5544
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user