ref(frontend): update to absolute import

This commit is contained in:
이강준
2022-11-26 17:12:36 +09:00
parent 001df70e26
commit 22c184840c
73 changed files with 2029 additions and 1287 deletions

View File

@@ -1,6 +1,6 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import checkAuth from "../pages/api/auth/CheckAuth"; import checkAuth from "~/pages/api/auth/CheckAuth";
import Image from "next/image"; import Image from "next/image";
import { publicPaths } from "../const"; import { publicPaths } from "../const";
@@ -79,4 +79,4 @@ export default function RouteGuard({ children }) {
</div> </div>
); );
} }
} }

View File

@@ -1,7 +1,7 @@
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
import { Fragment, useState } from "react"; import { Fragment, useState } from "react";
import InputField from "../InputField"; import InputField from "../InputField";
import addIncidentContact from "../../../pages/api/organization/addIncidentContact"; import addIncidentContact from "~/pages/api/organization/addIncidentContact";
import Button from "../buttons/Button"; import Button from "../buttons/Button";
const AddIncidentContactDialog = ({ const AddIncidentContactDialog = ({

View File

@@ -4,9 +4,9 @@ import ListBox from "../Listbox";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import Button from "../buttons/Button"; import Button from "../buttons/Button";
import InputField from "../InputField"; import InputField from "../InputField";
import getLatestFileKey from "../../../pages/api/workspace/getLatestFileKey"; import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey";
import { decryptAssymmetric, encryptAssymmetric } from "../../utilities/crypto"; import { decryptAssymmetric, encryptAssymmetric } from "../../utilities/crypto";
import addServiceToken from "../../../pages/api/serviceToken/addServiceToken"; import addServiceToken from "~/pages/api/serviceToken/addServiceToken";
import nacl from "tweetnacl"; import nacl from "tweetnacl";
import { faCheck, faCopy } from "@fortawesome/free-solid-svg-icons"; import { faCheck, faCopy } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@@ -28,7 +28,7 @@ const AddServiceTokenDialog = ({
isOpen, isOpen,
closeModal, closeModal,
workspaceId, workspaceId,
workspaceName workspaceName,
}) => { }) => {
const router = useRouter(); const router = useRouter();
const [serviceToken, setServiceToken] = useState(""); const [serviceToken, setServiceToken] = useState("");
@@ -38,52 +38,52 @@ const AddServiceTokenDialog = ({
const [serviceTokenCopied, setServiceTokenCopied] = useState(false); const [serviceTokenCopied, setServiceTokenCopied] = useState(false);
const generateServiceToken = async () => { const generateServiceToken = async () => {
const latestFileKey = await getLatestFileKey(workspaceId); const latestFileKey = await getLatestFileKey(workspaceId);
const key = decryptAssymmetric({ const key = decryptAssymmetric({
ciphertext: latestFileKey.latestKey.encryptedKey, ciphertext: latestFileKey.latestKey.encryptedKey,
nonce: latestFileKey.latestKey.nonce, nonce: latestFileKey.latestKey.nonce,
publicKey: latestFileKey.latestKey.sender.publicKey, publicKey: latestFileKey.latestKey.sender.publicKey,
privateKey: localStorage.getItem("PRIVATE_KEY") privateKey: localStorage.getItem("PRIVATE_KEY"),
}); });
// generate new public/private key pair // generate new public/private key pair
const pair = nacl.box.keyPair(); const pair = nacl.box.keyPair();
const publicKey = nacl.util.encodeBase64(pair.publicKey); const publicKey = nacl.util.encodeBase64(pair.publicKey);
const privateKey = nacl.util.encodeBase64(pair.secretKey); const privateKey = nacl.util.encodeBase64(pair.secretKey);
// encrypt workspace key under newly-generated public key // encrypt workspace key under newly-generated public key
const { ciphertext: encryptedKey, nonce } = encryptAssymmetric({ const { ciphertext: encryptedKey, nonce } = encryptAssymmetric({
plaintext: key, plaintext: key,
publicKey, publicKey,
privateKey privateKey,
}); });
let newServiceToken = await addServiceToken({ let newServiceToken = await addServiceToken({
name: serviceTokenName, name: serviceTokenName,
workspaceId, workspaceId,
environment: envMapping[serviceTokenEnv], environment: envMapping[serviceTokenEnv],
expiresIn: expiryMapping[serviceTokenExpiresIn], expiresIn: expiryMapping[serviceTokenExpiresIn],
publicKey, publicKey,
encryptedKey, encryptedKey,
nonce nonce,
}) });
const serviceToken = newServiceToken + ',' + privateKey; const serviceToken = newServiceToken + "," + privateKey;
setServiceToken(serviceToken); setServiceToken(serviceToken);
} };
function copyToClipboard() { function copyToClipboard() {
// Get the text field // Get the text field
var copyText = document.getElementById("serviceToken"); var copyText = document.getElementById("serviceToken");
// Select the text field // Select the text field
copyText.select(); copyText.select();
copyText.setSelectionRange(0, 99999); // For mobile devices 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); navigator.clipboard.writeText(copyText.value);
setServiceTokenCopied(true); setServiceTokenCopied(true);
setTimeout(() => setServiceTokenCopied(false), 2000); setTimeout(() => setServiceTokenCopied(false), 2000);
// Alert the copied text // Alert the copied text
@@ -94,7 +94,7 @@ const AddServiceTokenDialog = ({
closeModal(); closeModal();
setServiceTokenName(""); setServiceTokenName("");
setServiceToken(""); setServiceToken("");
} };
return ( return (
<div className="z-50"> <div className="z-50">
@@ -123,18 +123,25 @@ const AddServiceTokenDialog = ({
leaveFrom="opacity-100 scale-100" leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95" leaveTo="opacity-0 scale-95"
> >
{serviceToken == "" {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.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 <Dialog.Title
as="h3" as="h3"
className="text-lg font-medium leading-6 text-gray-400 z-50" 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> </Dialog.Title>
<div className="mt-2 mb-4"> <div className="mt-2 mb-4">
<div className="flex flex-col"> <div className="flex flex-col">
<p className="text-sm text-gray-500"> <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> </p>
</div> </div>
</div> </div>
@@ -154,7 +161,12 @@ const AddServiceTokenDialog = ({
<ListBox <ListBox
selected={serviceTokenEnv} selected={serviceTokenEnv}
onChange={setServiceTokenEnv} onChange={setServiceTokenEnv}
data={["Development", "Staging", "Production", "Testing"]} data={[
"Development",
"Staging",
"Production",
"Testing",
]}
width="full" width="full"
text="Environment: " text="Environment: "
/> />
@@ -162,8 +174,14 @@ const AddServiceTokenDialog = ({
<div className="max-h-28"> <div className="max-h-28">
<ListBox <ListBox
selected={serviceTokenExpiresIn} selected={serviceTokenExpiresIn}
onChange={setServiceTokenExpiresIn} onChange={
data={["1 day", "7 days", "1 month"]} setServiceTokenExpiresIn
}
data={[
"1 day",
"7 days",
"1 month",
]}
width="full" width="full"
text="Expires in: " text="Expires in: "
/> />
@@ -171,17 +189,24 @@ const AddServiceTokenDialog = ({
<div className="max-w-max"> <div className="max-w-max">
<div className="mt-6 flex flex-col justify-start w-max"> <div className="mt-6 flex flex-col justify-start w-max">
<Button <Button
onButtonPressed={() => generateServiceToken()} onButtonPressed={() =>
generateServiceToken()
}
color="mineshaft" color="mineshaft"
text="Add Service Token" text="Add Service Token"
textDisabled="Add Service Token" textDisabled="Add Service Token"
size="md" size="md"
active={serviceTokenName == "" ? false : true} active={
serviceTokenName == ""
? false
: true
}
/> />
</div> </div>
</div> </div>
</Dialog.Panel> </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 <Dialog.Title
as="h3" as="h3"
className="text-lg font-medium leading-6 text-gray-400 z-50" className="text-lg font-medium leading-6 text-gray-400 z-50"
@@ -191,17 +216,40 @@ const AddServiceTokenDialog = ({
<div className="mt-2 mb-4"> <div className="mt-2 mb-4">
<div className="flex flex-col"> <div className="flex flex-col">
<p className="text-sm text-gray-500"> <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> </p>
</div> </div>
</div> </div>
<div className="w-full"> <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"> <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> <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> 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"> <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"> <button
{serviceTokenCopied ? <FontAwesomeIcon icon={faCheck} className="pr-0.5"/> : <FontAwesomeIcon icon={faCopy} />} 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> </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"> <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 Click to Copy
@@ -211,14 +259,16 @@ const AddServiceTokenDialog = ({
</div> </div>
<div className="mt-6 flex flex-col justify-start w-max"> <div className="mt-6 flex flex-col justify-start w-max">
<Button <Button
onButtonPressed={() => closeAddServiceTokenModal()} onButtonPressed={() =>
closeAddServiceTokenModal()
}
color="mineshaft" color="mineshaft"
text="Close" text="Close"
size="md" size="md"
/> />
</div> </div>
</Dialog.Panel> </Dialog.Panel>
} )}
</Transition.Child> </Transition.Child>
</div> </div>
</div> </div>

View File

@@ -4,7 +4,7 @@ import { useEffect, useState } from "react";
import NavBarDashboard from "../navigation/NavBarDashboard"; import NavBarDashboard from "../navigation/NavBarDashboard";
import Listbox from "./Listbox"; import Listbox from "./Listbox";
import getWorkspaces from "../../pages/api/workspace/getWorkspaces"; import getWorkspaces from "~/pages/api/workspace/getWorkspaces";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { import {
faHouse, faHouse,
@@ -14,11 +14,11 @@ import {
faLink, faLink,
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
import AddWorkspaceDialog from "./dialog/AddWorkspaceDialog"; import AddWorkspaceDialog from "./dialog/AddWorkspaceDialog";
import createWorkspace from "../../pages/api/workspace/createWorkspace"; import createWorkspace from "~/pages/api/workspace/createWorkspace";
import getOrganizationUserProjects from "../../pages/api/organization/GetOrgUserProjects"; import getOrganizationUserProjects from "~/pages/api/organization/GetOrgUserProjects";
import getOrganizationUsers from "../../pages/api/organization/GetOrgUsers"; import getOrganizationUsers from "~/pages/api/organization/GetOrgUsers";
import addUserToWorkspace from "../../pages/api/workspace/addUserToWorkspace"; import addUserToWorkspace from "~/pages/api/workspace/addUserToWorkspace";
import getOrganizations from "../../pages/api/organization/getOrgs"; import getOrganizations from "~/pages/api/organization/getOrgs";
import { faPlus } from "@fortawesome/free-solid-svg-icons"; import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { decryptAssymmetric, encryptAssymmetric } from "../utilities/crypto"; import { decryptAssymmetric, encryptAssymmetric } from "../utilities/crypto";
@@ -48,9 +48,7 @@ export default function Layout({ children }) {
setLoading(true); setLoading(true);
setTimeout(() => setLoading(false), 1500); setTimeout(() => setLoading(false), 1500);
const workspaces = await getWorkspaces(); const workspaces = await getWorkspaces();
const currentWorkspaces = workspaces.map( const currentWorkspaces = workspaces.map((workspace) => workspace.name);
(workspace) => workspace.name
);
if (!currentWorkspaces.includes(workspaceName)) { if (!currentWorkspaces.includes(workspaceName)) {
const newWorkspace = await createWorkspace( const newWorkspace = await createWorkspace(
workspaceName, workspaceName,
@@ -141,7 +139,10 @@ export default function Layout({ children }) {
useEffect(async () => { useEffect(async () => {
// Put a user in a workspace if they're not in one yet // 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(); const userOrgs = await getOrganizations();
localStorage.setItem("orgData.id", userOrgs[0]._id); localStorage.setItem("orgData.id", userOrgs[0]._id);
} }
@@ -150,7 +151,11 @@ export default function Layout({ children }) {
orgId: localStorage.getItem("orgData.id"), orgId: localStorage.getItem("orgData.id"),
}); });
let userWorkspaces = orgUserProjects; 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"); router.push("/noprojects");
} else if (router.asPath != "/noprojects") { } else if (router.asPath != "/noprojects") {
const intendedWorkspaceId = router.asPath const intendedWorkspaceId = router.asPath
@@ -158,7 +163,9 @@ export default function Layout({ children }) {
[router.asPath.split("/").length - 1].split("?")[0]; [router.asPath.split("/").length - 1].split("?")[0];
// If a user is not a member of a workspace they are trying to access, just push them to one of theirs // If a user is not a member of a workspace they are trying to access, just push them to one of theirs
if (intendedWorkspaceId != "heroku" && !userWorkspaces if (
intendedWorkspaceId != "heroku" &&
!userWorkspaces
.map((workspace) => workspace._id) .map((workspace) => workspace._id)
.includes(intendedWorkspaceId) .includes(intendedWorkspaceId)
) { ) {
@@ -207,7 +214,10 @@ export default function Layout({ children }) {
workspaceMapping[workspaceSelected] + workspaceMapping[workspaceSelected] +
"?Development" "?Development"
); );
localStorage.setItem("projectData.id", workspaceMapping[workspaceSelected]) localStorage.setItem(
"projectData.id",
workspaceMapping[workspaceSelected]
);
} }
} catch (error) { } catch (error) {
console.log(error); console.log(error);
@@ -226,8 +236,8 @@ export default function Layout({ children }) {
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide"> <div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
PROJECT PROJECT
</div> </div>
{workspaceList.length>0 {workspaceList.length > 0 ? (
? <Listbox <Listbox
selected={workspaceSelected} selected={workspaceSelected}
onChange={setWorkspaceSelected} onChange={setWorkspaceSelected}
data={workspaceList} data={workspaceList}
@@ -235,58 +245,67 @@ export default function Layout({ children }) {
text="" text=""
workspaceMapping={workspaceMapping} workspaceMapping={workspaceMapping}
/> />
: <Button ) : (
<Button
text="Add Project" text="Add Project"
onButtonPressed={openModal} onButtonPressed={openModal}
color="mineshaft" color="mineshaft"
size="md" size="md"
icon={faPlus} icon={faPlus}
/> />
} )}
</div> </div>
<ul> <ul>
{workspaceList.length > 0 && menuItems.map(({ href, title, emoji }) => ( {workspaceList.length > 0 &&
<li className="mt-1.5 mx-2" key={title}> menuItems.map(({ href, title, emoji }) => (
{router.asPath.split("/")[1] === <li className="mt-1.5 mx-2" key={title}>
href.split("/")[1] && {router.asPath.split("/")[1] ===
(["project", "billing", "org", "personal"].includes( href.split("/")[1] &&
router.asPath.split("/")[2] ([
) "project",
? router.asPath.split("/")[2] === "billing",
href.split("/")[2] "org",
: true) ? ( "personal",
<div ].includes(
className={`flex p-2 text-white text-sm rounded cursor-pointer bg-mineshaft-50/10`} router.asPath.split("/")[2]
> )
<div className="bg-primary w-1 rounded-xl mr-1"></div> ? router.asPath.split(
<p className="ml-2 mr-4"> "/"
{emoji} )[2] === href.split("/")[2]
</p> : true) ? (
{title}
</div>
) : router.asPath == "/noprojects" ? (
<div
className={`flex p-2 text-white text-sm rounded`}
>
<p className="ml-2 mr-4">
{emoji}
</p>
{title}
</div>
) : (
<Link href={href}>
<div <div
className={`flex p-2 text-white text-sm rounded cursor-pointer hover:bg-mineshaft-50/5`} className={`flex 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"> <p className="ml-2 mr-4">
{emoji} {emoji}
</p> </p>
{title} {title}
</div> </div>
</Link> ) : (
)} <Link href={href}>
</li> <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> </ul>
</nav> </nav>
</aside> </aside>

View File

@@ -1,11 +1,11 @@
import React, { useEffect, useMemo, useState } from "react"; import React, { useEffect, useMemo, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import Listbox from "../Listbox"; import Listbox from "../Listbox";
import uploadKeys from "../../../pages/api/workspace/uploadKeys"; import uploadKeys from "~/pages/api/workspace/uploadKeys";
import getLatestFileKey from "../../../pages/api/workspace/getLatestFileKey"; import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey";
import deleteUserFromWorkspace from "../../../pages/api/workspace/deleteUserFromWorkspace"; import deleteUserFromWorkspace from "~/pages/api/workspace/deleteUserFromWorkspace";
import changeUserRoleInWorkspace from "../../../pages/api/workspace/changeUserRoleInWorkspace"; import changeUserRoleInWorkspace from "~/pages/api/workspace/changeUserRoleInWorkspace";
import deleteUserFromOrganization from "../../../pages/api/organization/deleteUserFromOrganization"; import deleteUserFromOrganization from "~/pages/api/organization/deleteUserFromOrganization";
import { faX } from "@fortawesome/free-solid-svg-icons"; import { faX } from "@fortawesome/free-solid-svg-icons";
import Button from "../buttons/Button"; import Button from "../buttons/Button";
import guidGenerator from "../../utilities/randomId"; import guidGenerator from "../../utilities/randomId";
@@ -145,7 +145,10 @@ const UserTable = ({
) )
.map((row, index) => { .map((row, index) => {
return ( 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"> <td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
{row.firstName} {row.firstName}
</td> </td>

View File

@@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import StripeRedirect from "../../pages/api/organization/StripeRedirect"; import StripeRedirect from "~/pages/api/organization/StripeRedirect";
export default function Plan({ plan }) { export default function Plan({ plan }) {
return ( return (
@@ -27,12 +27,12 @@ export default function Plan({ plan }) {
{plan.priceExplanation} {plan.priceExplanation}
</p> </p>
</div> </div>
<p className="relative z-10 max-w-fit px-6 text-base text-gray-400"> <p className="relative z-10 max-w-fit px-6 text-base text-gray-400">
{plan.text} {plan.text}
</p> </p>
<p className="relative z-10 max-w-fit px-6 text-base text-gray-400"> <p className="relative z-10 max-w-fit px-6 text-base text-gray-400">
{plan.subtext} {plan.subtext}
</p> </p>
</div> </div>
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
{plan.current == false ? ( {plan.current == false ? (
@@ -54,7 +54,17 @@ export default function Plan({ plan }) {
: "hover:bg-primary hover:text-black hover:border-primary" : "hover:bg-primary hover:text-black hover:border-primary"
} bg-bunker duration-200 cursor-pointer rounded-md flex w-max`} } 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> </div>
)} )}
<a href="/pricing" target='_blank rel="noopener"'> <a href="/pricing" target='_blank rel="noopener"'>

View File

@@ -3,7 +3,7 @@ import { useRouter } from "next/router";
import Image from "next/image"; import Image from "next/image";
import logout from "../../pages/api/auth/Logout"; import logout from "~/pages/api/auth/Logout";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCircleQuestion } from "@fortawesome/free-regular-svg-icons"; import { faCircleQuestion } from "@fortawesome/free-regular-svg-icons";
import { import {
@@ -12,14 +12,14 @@ import {
faCoins, faCoins,
faRightFromBracket, faRightFromBracket,
faEnvelope, faEnvelope,
faPlus, faPlus,
faAngleDown faAngleDown,
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
import { faSlack, faGithub } from "@fortawesome/free-brands-svg-icons"; import { faSlack, faGithub } from "@fortawesome/free-brands-svg-icons";
import { Menu, Transition } from "@headlessui/react"; import { Menu, Transition } from "@headlessui/react";
import getUser from "../../pages/api/user/getUser"; import getUser from "~/pages/api/user/getUser";
import getOrganizations from "../../pages/api/organization/getOrgs"; import getOrganizations from "~/pages/api/organization/getOrgs";
import getOrganization from "../../pages/api/organization/GetOrg"; import getOrganization from "~/pages/api/organization/GetOrg";
import guidGenerator from "../utilities/randomId"; import guidGenerator from "../utilities/randomId";
const supportOptions = [ const supportOptions = [
@@ -87,7 +87,7 @@ export default function Navbar({ onButtonPressed }) {
</div> </div>
<div className="relative flex justify-start items-center mx-2 z-40"> <div className="relative flex justify-start items-center mx-2 z-40">
<Menu as="div" className="relative inline-block text-left"> <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"> <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 <FontAwesomeIcon
className="text-xl" className="text-xl"
@@ -128,7 +128,10 @@ export default function Navbar({ onButtonPressed }) {
<div> <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"> <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} {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> </Menu.Button>
</div> </div>
<Transition <Transition
@@ -145,13 +148,15 @@ export default function Navbar({ onButtonPressed }) {
<div className="text-gray-400 self-start ml-2 mt-2 text-xs font-semibold tracking-wide"> <div className="text-gray-400 self-start ml-2 mt-2 text-xs font-semibold tracking-wide">
SIGNED IN AS SIGNED IN AS
</div> </div>
<div <div
onClick={() => onClick={() =>
router.push( 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"> <div className="bg-white/10 h-8 w-9 rounded-full flex items-center justify-center text-gray-300">
{user?.firstName?.charAt(0)} {user?.firstName?.charAt(0)}
</div> </div>
@@ -159,7 +164,8 @@ export default function Navbar({ onButtonPressed }) {
<div> <div>
<p className="text-gray-300 px-2 pt-1 text-sm"> <p className="text-gray-300 px-2 pt-1 text-sm">
{" "} {" "}
{user?.firstName} {user?.lastName} {user?.firstName}{" "}
{user?.lastName}
</p> </p>
<p className="text-gray-400 px-2 pb-1 text-xs"> <p className="text-gray-400 px-2 pb-1 text-xs">
{" "} {" "}
@@ -235,7 +241,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" 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"> <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> </span>
<div className="text-sm ml-1"> <div className="text-sm ml-1">
Invite Members Invite Members
@@ -243,43 +252,45 @@ export default function Navbar({ onButtonPressed }) {
</div> </div>
</button> </button>
</div> </div>
{orgs?.length > 1 && <div className="px-1 pt-1"> {orgs?.length > 1 && (
<div className="text-gray-400 self-start ml-2 mt-2 text-xs font-semibold tracking-wide"> <div className="px-1 pt-1">
OTHER ORGANIZATIONS <div className="text-gray-400 self-start ml-2 mt-2 text-xs font-semibold tracking-wide">
</div> OTHER ORGANIZATIONS
<div className="flex flex-col items-start px-1 mt-3 mb-2"> </div>
{orgs <div className="flex flex-col items-start px-1 mt-3 mb-2">
.filter( {orgs
(org) => .filter(
org._id != (org) =>
localStorage.getItem( org._id !=
"orgData.id" localStorage.getItem(
) "orgData.id"
) )
.map((org) => ( )
<div .map((org) => (
key={guidGenerator()} <div
onClick={() => { key={guidGenerator()}
localStorage.setItem( onClick={() => {
"orgData.id", localStorage.setItem(
org._id "orgData.id",
); org._id
router.reload(); );
}} router.reload();
className="flex flex-row justify-start items-center hover:bg-white/5 w-full p-1.5 cursor-pointer rounded-md" }}
> 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 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>
<div className="flex items-center justify-between w-full"> ))}
<p className="text-gray-300 px-2 text-sm"> </div>
{org.name}
</p>
</div>
</div>
))}
</div> </div>
</div>} )}
<div className="px-1 py-1"> <div className="px-1 py-1">
<Menu.Item> <Menu.Item>
{({ active }) => ( {({ active }) => (

View File

@@ -1,18 +1,21 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faAngleRight, faQuestionCircle } from "@fortawesome/free-solid-svg-icons"; import {
faAngleRight,
faQuestionCircle,
} from "@fortawesome/free-solid-svg-icons";
import { faCcMastercard, faCcVisa } from "@fortawesome/free-brands-svg-icons"; import { faCcMastercard, faCcVisa } from "@fortawesome/free-brands-svg-icons";
import { faCircle } from "@fortawesome/free-solid-svg-icons"; import { faCircle } from "@fortawesome/free-solid-svg-icons";
import getOrganization from "../../pages/api/organization/GetOrg"; import getOrganization from "~/pages/api/organization/GetOrg";
import getWorkspaceInfo from "../../pages/api/workspace/getWorkspaceInfo"; import getWorkspaceInfo from "~/pages/api/workspace/getWorkspaceInfo";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
export default function NavHeader({ pageName, isProjectRelated}) { export default function NavHeader({ pageName, isProjectRelated }) {
const [orgName, setOrgName] = useState(""); const [orgName, setOrgName] = useState("");
const [workspaceName, setWorkspaceName] = useState(""); const [workspaceName, setWorkspaceName] = useState("");
const router = useRouter(); const router = useRouter();
useEffect(async () => { useEffect(async () => {
let org = await getOrganization({ let org = await getOrganization({
orgId: localStorage.getItem("orgData.id"), orgId: localStorage.getItem("orgData.id"),
}); });
@@ -21,27 +24,30 @@ export default function NavHeader({ pageName, isProjectRelated}) {
workspaceId: router.query.id, workspaceId: router.query.id,
}); });
setWorkspaceName(workspace.name); setWorkspaceName(workspace.name);
}, []); }, []);
return ( return (
<div className="pt-20 ml-6 flex flex-row items-center"> <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"> <div className="bg-primary-900 h-6 w-6 rounded-md flex items-center justify-center text-mineshaft-100 mr-2">
{orgName?.charAt(0)} {orgName?.charAt(0)}
</div> </div>
<div className="text-primary text-sm font-semibold"> <div className="text-primary text-sm font-semibold">{orgName}</div>
{orgName} {isProjectRelated && (
</div> <>
{isProjectRelated && <> <FontAwesomeIcon
<FontAwesomeIcon icon={faAngleRight} className="ml-3 text-sm text-gray-400 mr-3" /> icon={faAngleRight}
<div className="font-semibold text-primary text-sm"> className="ml-3 text-sm text-gray-400 mr-3"
{workspaceName} />
</div> <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} <FontAwesomeIcon
</div> icon={faAngleRight}
</div> className="ml-3 text-sm text-gray-400 mr-3"
/>
<div className="text-gray-400 text-sm">{pageName}</div>
</div>
); );
} }

View File

@@ -1,29 +1,27 @@
import token from "../../pages/api/auth/Token" import token from "~/pages/api/auth/Token";
import { PATH } from '../../const'; import { PATH } from "../../const";
export default class SecurityClient { export default class SecurityClient {
static authOrigins = [PATH] static authOrigins = [PATH];
static #token = ''; static #token = "";
contructor() { contructor() {}
} static setToken(token) {
this.#token = token;
}
static setToken(token) { static async fetchCall(resource, options) {
this.#token = token; let req = new Request(resource, options);
} const destOrigin = new URL(req.url).origin;
static async fetchCall(resource, options) { if (this.#token == "") {
let req = new Request(resource, options); this.setToken(await token());
const destOrigin = new URL(req.url).origin; }
if (this.#token == "") { if (this.#token && this.authOrigins.includes(destOrigin)) {
this.setToken(await token()) 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);
}
}
}

View File

@@ -1,10 +1,10 @@
import login1 from "../../pages/api/auth/Login1"; import login1 from "~/pages/api/auth/Login1";
import login2 from "../../pages/api/auth/Login2"; import login2 from "~/pages/api/auth/Login2";
import Aes256Gcm from "../../components/aes-256-gcm"; import Aes256Gcm from "~/components/aes-256-gcm";
import pushKeys from "./pushKeys"; import pushKeys from "./pushKeys";
import { initPostHog } from "../analytics/posthog"; import { initPostHog } from "../analytics/posthog";
import getOrganizations from "../../pages/api/organization/getOrgs"; import getOrganizations from "~/pages/api/organization/getOrgs";
import getOrganizationUserProjects from "../../pages/api/organization/GetOrgUserProjects"; import getOrganizationUserProjects from "~/pages/api/organization/GetOrgUserProjects";
import SecurityClient from "./SecurityClient"; import SecurityClient from "./SecurityClient";
import { ENV } from "./config"; import { ENV } from "./config";
@@ -77,7 +77,14 @@ const attemptLogin = async (
encryptedPrivateKey, encryptedPrivateKey,
iv, iv,
tag, 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 { try {
@@ -101,10 +108,14 @@ const attemptLogin = async (
} }
const userOrgs = await getOrganizations(); const userOrgs = await getOrganizations();
const userOrgsData = userOrgs.map(org => org._id); const userOrgsData = userOrgs.map((org) => org._id);
let orgToLogin; let orgToLogin;
if (userOrgsData.includes(localStorage.getItem("orgData.id"))) { if (
userOrgsData.includes(
localStorage.getItem("orgData.id")
)
) {
orgToLogin = localStorage.getItem("orgData.id"); orgToLogin = localStorage.getItem("orgData.id");
} else { } else {
orgToLogin = userOrgsData[0]; orgToLogin = userOrgsData[0];
@@ -114,17 +125,29 @@ const attemptLogin = async (
let orgUserProjects = await getOrganizationUserProjects({ let orgUserProjects = await getOrganizationUserProjects({
orgId: orgToLogin, orgId: orgToLogin,
}); });
orgUserProjects = orgUserProjects?.map(project => project._id); orgUserProjects = orgUserProjects?.map(
(project) => project._id
);
let projectToLogin; let projectToLogin;
if (orgUserProjects.includes(localStorage.getItem("projectData.id"))) { if (
orgUserProjects.includes(
localStorage.getItem("projectData.id")
)
) {
projectToLogin = localStorage.getItem("projectData.id"); projectToLogin = localStorage.getItem("projectData.id");
} else { } else {
try { try {
projectToLogin = orgUserProjects[0]; projectToLogin = orgUserProjects[0];
localStorage.setItem("projectData.id", projectToLogin); localStorage.setItem(
"projectData.id",
projectToLogin
);
} catch (error) { } catch (error) {
console.log("ERROR: User likely has no projects. ", error) console.log(
"ERROR: User likely has no projects. ",
error
);
} }
} }

View File

@@ -1,6 +1,6 @@
import Aes256Gcm from "../aes-256-gcm"; import Aes256Gcm from "../aes-256-gcm";
import SRP1 from "../../pages/api/auth/SRP1"; import SRP1 from "~/pages/api/auth/SRP1";
import changePassword2 from "../../pages/api/auth/ChangePassword2"; import changePassword2 from "~/pages/api/auth/ChangePassword2";
const nacl = require("tweetnacl"); const nacl = require("tweetnacl");
nacl.util = require("tweetnacl-util"); nacl.util = require("tweetnacl-util");
@@ -41,7 +41,7 @@ const changePassword = async (
let serverPublicKey, salt; let serverPublicKey, salt;
try { try {
const res = await SRP1({ const res = await SRP1({
clientPublicKey: clientPublicKey clientPublicKey: clientPublicKey,
}); });
serverPublicKey = res.serverPublicKey; serverPublicKey = res.serverPublicKey;
salt = res.salt; salt = res.salt;
@@ -54,54 +54,70 @@ const changePassword = async (
clientOldPassword.setServerPublicKey(serverPublicKey); clientOldPassword.setServerPublicKey(serverPublicKey);
const clientProof = clientOldPassword.getProof(); // called M1 const clientProof = clientOldPassword.getProof(); // called M1
clientNewPassword.init({ clientNewPassword.init(
username: email, {
password: newPassword username: email,
}, async () => { password: newPassword,
clientNewPassword.createVerifier(async (err, result) => { },
async () => {
let { ciphertext, iv, tag } = Aes256Gcm.encrypt( clientNewPassword.createVerifier(
localStorage.getItem("PRIVATE_KEY"), async (err, result) => {
newPassword.slice(0, 32).padStart(32 + (newPassword.slice(0, 32).length - new Blob([newPassword]).size), "0") 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) { if (ciphertext) {
localStorage.setItem( localStorage.setItem(
"encryptedPrivateKey", "encryptedPrivateKey",
ciphertext ciphertext
); );
localStorage.setItem("iv", iv); localStorage.setItem("iv", iv);
localStorage.setItem("tag", tag); localStorage.setItem("tag", tag);
let res; let res;
try { try {
res = await changePassword2({ res = await changePassword2({
encryptedPrivateKey: ciphertext, encryptedPrivateKey: ciphertext,
iv, iv,
tag, tag,
salt: result.salt, salt: result.salt,
verifier: result.verifier, verifier: result.verifier,
clientProof clientProof,
}); });
if (res.status == 400) { if (res.status == 400) {
setCurrentPasswordError(true); setCurrentPasswordError(true);
} else if (res.status == 200) { } else if (res.status == 200) {
setPasswordChanged(true); setPasswordChanged(true);
setCurrentPassword(""); setCurrentPassword("");
setNewPassword(""); setNewPassword("");
}
} catch (err) {
setCurrentPasswordError(true);
console.log(err);
}
} }
} catch (err) {
setCurrentPasswordError(true)
console.log(err);
} }
} );
}); }
}); );
} }
); );
} catch (error) { } 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; return true;
}; };

View File

@@ -1,7 +1,6 @@
import getSecrets from "../../pages/api/files/GetSecrets"; import getSecrets from "~/pages/api/files/GetSecrets";
import guidGenerator from "./randomId"; import guidGenerator from "./randomId";
const { const {
decryptAssymmetric, decryptAssymmetric,
decryptSymmetric, decryptSymmetric,
@@ -21,15 +20,12 @@ const getSecretsForProject = async ({
setFileState, setFileState,
setIsKeyAvailable, setIsKeyAvailable,
setData, setData,
workspaceId workspaceId,
}) => { }) => {
try { try {
let file; let file;
try { try {
file = await getSecrets( file = await getSecrets(workspaceId, envMapping[env]);
workspaceId,
envMapping[env]
);
setFileState(file); setFileState(file);
} catch (error) { } catch (error) {
@@ -98,7 +94,9 @@ const getSecretsForProject = async ({
line["type"], line["type"],
]); ]);
} catch (error) { } catch (error) {
console.log("Something went wrong during accessing or decripting secrets."); console.log(
"Something went wrong during accessing or decripting secrets."
);
} }
return true; return true;
}; };

View File

@@ -1,6 +1,6 @@
import Aes256Gcm from "../aes-256-gcm"; import Aes256Gcm from "../aes-256-gcm";
import SRP1 from "../../pages/api/auth/SRP1"; import SRP1 from "~/pages/api/auth/SRP1";
import issueBackupPrivateKey from "../../pages/api/auth/IssueBackupPrivateKey"; import issueBackupPrivateKey from "~/pages/api/auth/IssueBackupPrivateKey";
import generateBackupPDF from "./generateBackupPDF"; import generateBackupPDF from "./generateBackupPDF";
const nacl = require("tweetnacl"); const nacl = require("tweetnacl");
@@ -24,7 +24,7 @@ const issueBackupKey = async ({
password, password,
personalName, personalName,
setBackupKeyError, setBackupKeyError,
setBackupKeyIssued setBackupKeyIssued,
}) => { }) => {
try { try {
setBackupKeyError(false); setBackupKeyError(false);
@@ -40,7 +40,7 @@ const issueBackupKey = async ({
let serverPublicKey, salt; let serverPublicKey, salt;
try { try {
const res = await SRP1({ const res = await SRP1({
clientPublicKey: clientPublicKey clientPublicKey: clientPublicKey,
}); });
serverPublicKey = res.serverPublicKey; serverPublicKey = res.serverPublicKey;
salt = res.salt; salt = res.salt;
@@ -55,35 +55,40 @@ const issueBackupKey = async ({
const generatedKey = crypto.randomBytes(16).toString("hex"); const generatedKey = crypto.randomBytes(16).toString("hex");
clientKey.init({ clientKey.init(
username: email, {
password: generatedKey username: email,
}, async () => { password: generatedKey,
clientKey.createVerifier(async (err, result) => { },
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( if (res.status == 400) {
localStorage.getItem("PRIVATE_KEY"), setBackupKeyError(true);
generatedKey } else if (res.status == 200) {
); generateBackupPDF(
personalName,
const res = await issueBackupPrivateKey({ email,
encryptedPrivateKey: ciphertext, generatedKey
iv, );
tag, setBackupKeyIssued(true);
salt: result.salt, }
verifier: result.verifier,
clientProof
}); });
}
if (res.status == 400) { );
setBackupKeyError(true);
} else if (res.status == 200) {
generateBackupPDF(personalName, email, generatedKey);
setBackupKeyIssued(true);
}
});
})
} }
); );
} catch (error) { } catch (error) {

View File

@@ -1,6 +1,6 @@
import getLatestFileKey from "../../pages/api/workspace/getLatestFileKey"; import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey";
import getWorkspaceKeys from "../../pages/api/workspace/getWorkspaceKeys"; import getWorkspaceKeys from "~/pages/api/workspace/getWorkspaceKeys";
import uploadSecrets from "../../pages/api/files/UploadSecrets"; import uploadSecrets from "~/pages/api/files/UploadSecrets";
const crypto = require("crypto"); const crypto = require("crypto");
const { const {

View File

@@ -1,11 +1,8 @@
import publicKeyInfical from "../../pages/api/auth/publicKeyInfisical" import publicKeyInfical from "~/pages/api/auth/publicKeyInfisical";
import changeHerokuConfigVars from "../../pages/api/integrations/ChangeHerokuConfigVars"; import changeHerokuConfigVars from "~/pages/api/integrations/ChangeHerokuConfigVars";
const crypto = require("crypto"); const crypto = require("crypto");
const { const { encryptSymmetric, encryptAssymmetric } = require("./crypto");
encryptSymmetric,
encryptAssymmetric,
} = require("./crypto");
const nacl = require("tweetnacl"); const nacl = require("tweetnacl");
nacl.util = require("tweetnacl-util"); nacl.util = require("tweetnacl-util");
@@ -71,7 +68,7 @@ const pushKeysIntegration = async ({ obj, integrationId }) => {
nonce, nonce,
}; };
changeHerokuConfigVars({integrationId, key, secrets}) changeHerokuConfigVars({ integrationId, key, secrets });
}; };
export default pushKeysIntegration; export default pushKeysIntegration;

View File

@@ -6,10 +6,10 @@
"components/*" "components/*"
], ],
"~/utilities/*": [ "~/utilities/*": [
"components/utilities" "components/utilities/*"
], ],
"~/pages/*": [ "~/pages/*": [
"components/*" "pages/*"
], ],
} }
} }

View File

@@ -1,13 +1,13 @@
import "../styles/globals.css"; import "../styles/globals.css";
import "@fortawesome/fontawesome-svg-core/styles.css"; import "@fortawesome/fontawesome-svg-core/styles.css";
import { config } from "@fortawesome/fontawesome-svg-core"; import { config } from "@fortawesome/fontawesome-svg-core";
import Layout from "../components/basic/layout"; import Layout from "~/components/basic/layout";
import RouteGuard from "../components/RouteGuard"; import RouteGuard from "~/components/RouteGuard";
import { publicPaths } from "../const.js"; import { publicPaths } from "../const.js";
import { useEffect } from "react"; import { useEffect } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { initPostHog } from "../components/analytics/posthog"; import { initPostHog } from "~/components/analytics/posthog";
import { ENV } from "../components/utilities/config"; import { ENV } from "~/utilities/config";
config.autoAddCss = false; config.autoAddCss = false;
@@ -39,9 +39,7 @@ const App = ({ Component, pageProps, ...appProps }) => {
publicPaths.includes("/" + appProps.router.pathname.split("/")[1]) || publicPaths.includes("/" + appProps.router.pathname.split("/")[1]) ||
!Component.requireAuth !Component.requireAuth
) { ) {
return ( return <Component {...pageProps} />;
<Component {...pageProps} />
)
} }
return ( return (

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -6,28 +6,34 @@ import { PATH } from "../../../const";
* @param {*} clientPublicKey * @param {*} clientPublicKey
* @returns * @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", { return SecurityClient.fetchCall(PATH + "/api/v1/password/change-password", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
"clientProof": clientProof, clientProof: clientProof,
"encryptedPrivateKey": encryptedPrivateKey, encryptedPrivateKey: encryptedPrivateKey,
"iv": iv, iv: iv,
"tag": tag, tag: tag,
"salt": salt, salt: salt,
"verifier": verifier verifier: verifier,
}), }),
}) }).then(async (res) => {
.then(async res => {
if (res.status == 200) { if (res.status == 200) {
return res; return res;
} else { } else {
console.log('Failed to change the password'); console.log("Failed to change the password");
} }
}) });
}; };
export default changePassword2; export default changePassword2;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient.js"; import SecurityClient from "~/utilities/SecurityClient.js";
import { PATH } from "../../../const.js"; import { PATH } from "../../../const.js";
/** /**
@@ -14,14 +14,13 @@ const checkAuth = async (req, res) => {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}) }).then((res) => {
.then(res => {
if (res.status == 200) { if (res.status == 200) {
return res; return res;
} else { } else {
console.log("Not authorized"); console.log("Not authorized");
} }
}) });
}; };
export default checkAuth; export default checkAuth;

View File

@@ -1,32 +1,41 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
* This is the route that issues a backup private key that will afterwards be added into a pdf * 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}) => { const issueBackupPrivateKey = ({
return SecurityClient.fetchCall(PATH + "/api/v1/password/backup-private-key", { encryptedPrivateKey,
method: "POST", iv,
headers: { tag,
"Content-Type": "application/json", salt,
}, verifier,
body: JSON.stringify({ clientProof,
"clientProof": clientProof, }) => {
"encryptedPrivateKey": encryptedPrivateKey, return SecurityClient.fetchCall(
"iv": iv, PATH + "/api/v1/password/backup-private-key",
"tag": tag, {
"salt": salt, method: "POST",
"verifier": verifier headers: {
}), "Content-Type": "application/json",
}) },
.then(res => { body: JSON.stringify({
clientProof: clientProof,
encryptedPrivateKey: encryptedPrivateKey,
iv: iv,
tag: tag,
salt: salt,
verifier: verifier,
}),
}
).then((res) => {
if (res.status == 200) { if (res.status == 200) {
return res; return res;
} else { } else {
return res; return res;
console.log("Failed to issue the backup key"); console.log("Failed to issue the backup key");
} }
}) });
}; };
export default issueBackupPrivateKey; export default issueBackupPrivateKey;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -14,9 +14,8 @@ const logout = async (req, res) => {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
credentials: "include" credentials: "include",
}) }).then((res) => {
.then(res => {
if (res.status == 200) { if (res.status == 200) {
SecurityClient.setToken(""); SecurityClient.setToken("");
// Delete the cookie by not setting a value; Alternatively clear the local storage // Delete the cookie by not setting a value; Alternatively clear the local storage
@@ -30,7 +29,7 @@ const logout = async (req, res) => {
} else { } else {
console.log("Failed to log out"); console.log("Failed to log out");
} }
}) });
}; };
export default logout; export default logout;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -6,7 +6,7 @@ import { PATH } from "../../../const";
* @param {*} clientPublicKey * @param {*} clientPublicKey
* @returns * @returns
*/ */
const SRP1 = ({clientPublicKey}) => { const SRP1 = ({ clientPublicKey }) => {
return SecurityClient.fetchCall(PATH + "/api/v1/password/srp1", { return SecurityClient.fetchCall(PATH + "/api/v1/password/srp1", {
method: "POST", method: "POST",
headers: { headers: {
@@ -15,14 +15,13 @@ const SRP1 = ({clientPublicKey}) => {
body: JSON.stringify({ body: JSON.stringify({
clientPublicKey, clientPublicKey,
}), }),
}) }).then(async (res) => {
.then(async res => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()); return await res.json();
} else { } else {
console.log('Failed to do the first step of SRP'); console.log("Failed to do the first step of SRP");
} }
}) });
}; };
export default SRP1; export default SRP1;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient.js"; import SecurityClient from "~/utilities/SecurityClient.js";
import { PATH } from "../../../const.js"; import { PATH } from "../../../const.js";
/** /**
@@ -8,26 +8,28 @@ import { PATH } from "../../../const.js";
* @returns * @returns
*/ */
const getSecrets = async (workspaceId, env) => { const getSecrets = async (workspaceId, env) => {
return SecurityClient.fetchCall(PATH + "/api/v1/secret/" + return SecurityClient.fetchCall(
workspaceId + PATH +
"?" + "/api/v1/secret/" +
new URLSearchParams({ workspaceId +
environment: env, "?" +
channel: "web", new URLSearchParams({
environment: env,
channel: "web",
}),
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
} }
), { ).then(async (res) => {
method: "GET",
headers: {
"Content-Type": "application/json",
},
})
.then(async res => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()); return await res.json();
} else { } else {
console.log('Failed to get project secrets'); console.log("Failed to get project secrets");
} }
}) });
}; };
export default getSecrets; export default getSecrets;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -19,14 +19,13 @@ const uploadSecrets = async ({ workspaceId, secrets, keys, environment }) => {
environment, environment,
channel: "web", channel: "web",
}), }),
}) }).then(async (res) => {
.then(async res => {
if (res.status == 200) { if (res.status == 200) {
return res; return res;
} else { } else {
console.log('Failed to push secrets'); console.log("Failed to push secrets");
} }
}) });
}; };
export default uploadSecrets; export default uploadSecrets;

View File

@@ -1,24 +1,26 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
const changeHerokuConfigVars = ({ integrationId, key, secrets }) => { const changeHerokuConfigVars = ({ integrationId, key, secrets }) => {
return SecurityClient.fetchCall(PATH + "/api/v1/integration/" + integrationId + "/sync", { return SecurityClient.fetchCall(
method: "POST", PATH + "/api/v1/integration/" + integrationId + "/sync",
headers: { {
"Content-Type": "application/json", method: "POST",
}, headers: {
body: JSON.stringify({ "Content-Type": "application/json",
key, },
secrets, body: JSON.stringify({
}) key,
}) secrets,
.then(async res => { }),
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return res; return res;
} else { } else {
console.log('Failed to sync secrets to Heroku'); console.log("Failed to sync secrets to Heroku");
} }
}) });
}; };
export default changeHerokuConfigVars; export default changeHerokuConfigVars;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -7,19 +7,21 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const deleteIntegration = ({ integrationId }) => { const deleteIntegration = ({ integrationId }) => {
return SecurityClient.fetchCall(PATH + "/api/v1/integration/" + integrationId, { return SecurityClient.fetchCall(
method: "DELETE", PATH + "/api/v1/integration/" + integrationId,
headers: { {
"Content-Type": "application/json", method: "DELETE",
}, headers: {
}) "Content-Type": "application/json",
.then(async res => { },
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).workspace; return (await res.json()).workspace;
} else { } else {
console.log('Failed to delete an integration'); console.log("Failed to delete an integration");
} }
}) });
}; };
export default deleteIntegration; export default deleteIntegration;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -7,19 +7,21 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const deleteIntegrationAuth = ({ integrationAuthId }) => { const deleteIntegrationAuth = ({ integrationAuthId }) => {
return SecurityClient.fetchCall(PATH + "/api/v1/integration-auth/" + integrationAuthId, { return SecurityClient.fetchCall(
method: "DELETE", PATH + "/api/v1/integration-auth/" + integrationAuthId,
headers: { {
"Content-Type": "application/json", method: "DELETE",
}, headers: {
}) "Content-Type": "application/json",
.then(async res => { },
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return res; return res;
} else { } else {
console.log('Failed to delete an integration authorization'); console.log("Failed to delete an integration authorization");
} }
}) });
}; };
export default deleteIntegrationAuth; export default deleteIntegrationAuth;

View File

@@ -1,20 +1,22 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
const getIntegrationApps = ({ integrationAuthId }) => { const getIntegrationApps = ({ integrationAuthId }) => {
return SecurityClient.fetchCall(PATH + "/api/v1/integration-auth/" + integrationAuthId + "/apps", { return SecurityClient.fetchCall(
method: "GET", PATH + "/api/v1/integration-auth/" + integrationAuthId + "/apps",
headers: { {
"Content-Type": "application/json", method: "GET",
}, headers: {
}) "Content-Type": "application/json",
.then(async res => { },
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).apps; return (await res.json()).apps;
} else { } else {
console.log('Failed to get available apps for an integration'); console.log("Failed to get available apps for an integration");
} }
}) });
}; };
export default getIntegrationApps; export default getIntegrationApps;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
const getIntegrations = () => { const getIntegrations = () => {
@@ -7,14 +7,13 @@ const getIntegrations = () => {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}) }).then(async (res) => {
.then(async res => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).integrations; return (await res.json()).integrations;
} else { } else {
console.log('Failed to get project integrations'); console.log("Failed to get project integrations");
} }
}) });
}; };
export default getIntegrations; export default getIntegrations;

View File

@@ -1,32 +1,34 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; 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 * @param {*} integrationId
* @returns * @returns
*/ */
const startIntegration = ({ integrationId, appName, environment }) => { const startIntegration = ({ integrationId, appName, environment }) => {
return SecurityClient.fetchCall(PATH + "/api/v1/integration/" + integrationId, { return SecurityClient.fetchCall(
method: "PATCH", PATH + "/api/v1/integration/" + integrationId,
headers: { {
"Content-Type": "application/json", method: "PATCH",
}, headers: {
body: JSON.stringify({ "Content-Type": "application/json",
"update": { },
app: appName, body: JSON.stringify({
environment, update: {
isActive: true app: appName,
} environment,
}) isActive: true,
}) },
.then(async res => { }),
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return res; return res;
} else { } else {
console.log('Failed to start an integration'); console.log("Failed to start an integration");
} }
}) });
}; };
export default startIntegration; export default startIntegration;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -7,24 +7,26 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const AuthorizeIntegration = ({ workspaceId, code, integration }) => { const AuthorizeIntegration = ({ workspaceId, code, integration }) => {
return SecurityClient.fetchCall(PATH + "/api/v1/integration-auth/oauth-token", { return SecurityClient.fetchCall(
method: "POST", PATH + "/api/v1/integration-auth/oauth-token",
headers: { {
"Content-Type": "application/json", method: "POST",
}, headers: {
body: JSON.stringify({ "Content-Type": "application/json",
workspaceId, },
code, body: JSON.stringify({
integration workspaceId,
}), code,
}) integration,
.then(async res => { }),
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return res; return res;
} else { } else {
console.log('Failed to authorize the integration'); console.log("Failed to authorize the integration");
} }
}) });
}; };
export default AuthorizeIntegration; export default AuthorizeIntegration;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -7,19 +7,21 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const getWorkspaceAuthorizations = ({ workspaceId }) => { const getWorkspaceAuthorizations = ({ workspaceId }) => {
return SecurityClient.fetchCall(PATH + "/api/v1/workspace/" + workspaceId + "/authorizations", { return SecurityClient.fetchCall(
method: "GET", PATH + "/api/v1/workspace/" + workspaceId + "/authorizations",
headers: { {
"Content-Type": "application/json", method: "GET",
}, headers: {
}) "Content-Type": "application/json",
.then(async res => { },
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).authorizations; return (await res.json()).authorizations;
} else { } else {
console.log('Failed to get project authorizations'); console.log("Failed to get project authorizations");
} }
}) });
}; };
export default getWorkspaceAuthorizations; export default getWorkspaceAuthorizations;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -7,19 +7,21 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const getWorkspaceIntegrations = ({ workspaceId }) => { const getWorkspaceIntegrations = ({ workspaceId }) => {
return SecurityClient.fetchCall(PATH + "/api/v1/workspace/" + workspaceId + "/integrations", { return SecurityClient.fetchCall(
method: "GET", PATH + "/api/v1/workspace/" + workspaceId + "/integrations",
headers: { {
"Content-Type": "application/json", method: "GET",
}, headers: {
}) "Content-Type": "application/json",
.then(async res => { },
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).integrations; return (await res.json()).integrations;
} else { } else {
console.log('Failed to get the project integrations'); console.log("Failed to get the project integrations");
} }
}) });
}; };
export default getWorkspaceIntegrations; export default getWorkspaceIntegrations;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -8,19 +8,21 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const getOrganization = (req, res) => { const getOrganization = (req, res) => {
return SecurityClient.fetchCall(PATH + "/api/v1/organization/" + req.orgId, { return SecurityClient.fetchCall(
method: "GET", PATH + "/api/v1/organization/" + req.orgId,
headers: { {
"Content-Type": "application/json", method: "GET",
}, headers: {
}) "Content-Type": "application/json",
.then(async res => { },
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).organization; return (await res.json()).organization;
} else { } else {
console.log('Failed to get org info'); console.log("Failed to get org info");
} }
}) });
}; };
export default getOrganization; export default getOrganization;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -8,19 +8,21 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const getOrganizationProjects = (req, res) => { const getOrganizationProjects = (req, res) => {
return SecurityClient.fetchCall(PATH + "/api/organization/" + req.orgId + "/workspaces", { return SecurityClient.fetchCall(
method: "GET", PATH + "/api/organization/" + req.orgId + "/workspaces",
headers: { {
"Content-Type": "application/json", method: "GET",
}, headers: {
}) "Content-Type": "application/json",
.then(async res => { },
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).workspaces; return (await res.json()).workspaces;
} else { } else {
console.log('Failed to get projects for an org'); console.log("Failed to get projects for an org");
} }
}) });
}; };
export default getOrganizationProjects; export default getOrganizationProjects;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -8,19 +8,21 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const getOrganizationSubscriptions = (req, res) => { const getOrganizationSubscriptions = (req, res) => {
return SecurityClient.fetchCall(PATH + "/api/v1/organization/" + req.orgId + "/subscriptions", { return SecurityClient.fetchCall(
method: "GET", PATH + "/api/v1/organization/" + req.orgId + "/subscriptions",
headers: { {
"Content-Type": "application/json", method: "GET",
}, headers: {
}) "Content-Type": "application/json",
.then(async res => { },
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).subscriptions; return (await res.json()).subscriptions;
} else { } else {
console.log('Failed to get org subscriptions'); console.log("Failed to get org subscriptions");
} }
}) });
}; };
export default getOrganizationSubscriptions; export default getOrganizationSubscriptions;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -8,19 +8,21 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const getOrganizationUserProjects = (req, res) => { const getOrganizationUserProjects = (req, res) => {
return SecurityClient.fetchCall(PATH + "/api/v1/organization/" + req.orgId + "/my-workspaces", { return SecurityClient.fetchCall(
method: "GET", PATH + "/api/v1/organization/" + req.orgId + "/my-workspaces",
headers: { {
"Content-Type": "application/json", method: "GET",
}, headers: {
}) "Content-Type": "application/json",
.then(async res => { },
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).workspaces; return (await res.json()).workspaces;
} else { } 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; export default getOrganizationUserProjects;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -8,19 +8,21 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const getOrganizationUsers = (req, res) => { const getOrganizationUsers = (req, res) => {
return SecurityClient.fetchCall(PATH + "/api/v1/organization/" + req.orgId + "/users", { return SecurityClient.fetchCall(
method: "GET", PATH + "/api/v1/organization/" + req.orgId + "/users",
headers: { {
"Content-Type": "application/json", method: "GET",
}, headers: {
}) "Content-Type": "application/json",
.then(async res => { },
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).users; return (await res.json()).users;
} else { } else {
console.log('Failed to get org users'); console.log("Failed to get org users");
} }
}) });
}; };
export default getOrganizationUsers; export default getOrganizationUsers;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -7,20 +7,22 @@ import { PATH } from "../../../const";
* @param {*} res * @param {*} res
* @returns * @returns
*/ */
const StripeRedirect = ({orgId}) => { const StripeRedirect = ({ orgId }) => {
return SecurityClient.fetchCall(PATH + "/api/v1/organization/" + orgId + "/customer-portal-session", { return SecurityClient.fetchCall(
method: "POST", PATH + "/api/v1/organization/" + orgId + "/customer-portal-session",
headers: { {
"Content-Type": "application/json", 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');
} }
}) ).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; export default StripeRedirect;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -7,22 +7,24 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const addIncidentContact = (organizationId, email) => { const addIncidentContact = (organizationId, email) => {
return SecurityClient.fetchCall(PATH + "/api/v1/organization/" + organizationId + "/incidentContactOrg", { return SecurityClient.fetchCall(
method: "POST", PATH + "/api/v1/organization/" + organizationId + "/incidentContactOrg",
headers: { {
"Content-Type": "application/json", method: "POST",
}, headers: {
body: JSON.stringify({ "Content-Type": "application/json",
email: email, },
}), body: JSON.stringify({
}) email: email,
.then(async res => { }),
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return res; return res;
} else { } else {
console.log('Failed to add an incident contact'); console.log("Failed to add an incident contact");
} }
}) });
}; };
export default addIncidentContact; export default addIncidentContact;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -17,14 +17,13 @@ const addUserToOrg = (email, orgId) => {
inviteeEmail: email, inviteeEmail: email,
organizationId: orgId, organizationId: orgId,
}), }),
}) }).then(async (res) => {
.then(async res => {
if (res.status == 200) { if (res.status == 200) {
return res; return res;
} else { } else {
console.log('Failed to add a user to an org'); console.log("Failed to add a user to an org");
} }
}) });
}; };
export default addUserToOrg; export default addUserToOrg;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -7,22 +7,24 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const deleteIncidentContact = (organizaionId, email) => { const deleteIncidentContact = (organizaionId, email) => {
return SecurityClient.fetchCall(PATH + "/api/v1/organization/" + organizaionId + "/incidentContactOrg", { return SecurityClient.fetchCall(
method: "DELETE", PATH + "/api/v1/organization/" + organizaionId + "/incidentContactOrg",
headers: { {
"Content-Type": "application/json", method: "DELETE",
}, headers: {
body: JSON.stringify({ "Content-Type": "application/json",
email: email, },
}), body: JSON.stringify({
}) email: email,
.then(async res => { }),
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return res; return res;
} else { } else {
console.log('Failed to delete an incident contact'); console.log("Failed to delete an incident contact");
} }
}) });
}; };
export default deleteIncidentContact; export default deleteIncidentContact;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -7,19 +7,21 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const deleteUserFromOrganization = (membershipId) => { const deleteUserFromOrganization = (membershipId) => {
return SecurityClient.fetchCall(PATH + "/api/v1/membership-org/" + membershipId, { return SecurityClient.fetchCall(
method: "DELETE", PATH + "/api/v1/membership-org/" + membershipId,
headers: { {
"Content-Type": "application/json", method: "DELETE",
}, headers: {
}) "Content-Type": "application/json",
.then(async res => { },
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return res; return res;
} else { } else {
console.log('Failed to delete a user from an org'); console.log("Failed to delete a user from an org");
} }
}) });
}; };
export default deleteUserFromOrganization; export default deleteUserFromOrganization;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -7,19 +7,21 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const getIncidentContacts = (organizationId) => { const getIncidentContacts = (organizationId) => {
return SecurityClient.fetchCall(PATH + "/api/v1/organization/" + organizationId + "/incidentContactOrg", { return SecurityClient.fetchCall(
method: "GET", PATH + "/api/v1/organization/" + organizationId + "/incidentContactOrg",
headers: { {
"Content-Type": "application/json", method: "GET",
}, headers: {
}) "Content-Type": "application/json",
.then(async res => { },
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).incidentContactsOrg; return (await res.json()).incidentContactsOrg;
} else { } else {
console.log('Failed to get incident contacts'); console.log("Failed to get incident contacts");
} }
}) });
}; };
export default getIncidentContacts; export default getIncidentContacts;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -13,14 +13,13 @@ const getOrganizations = (req, res) => {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}) }).then(async (res) => {
.then(async res => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).organizations; return (await res.json()).organizations;
} else { } else {
console.log('Failed to get orgs of a user'); console.log("Failed to get orgs of a user");
} }
}) });
}; };
export default getOrganizations; export default getOrganizations;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -8,22 +8,24 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const renameOrg = (orgId, newOrgName) => { const renameOrg = (orgId, newOrgName) => {
return SecurityClient.fetchCall(PATH + "/api/v1/organization/" + orgId + "/name", { return SecurityClient.fetchCall(
method: "PATCH", PATH + "/api/v1/organization/" + orgId + "/name",
headers: { {
"Content-Type": "application/json", method: "PATCH",
}, headers: {
body: JSON.stringify({ "Content-Type": "application/json",
name: newOrgName, },
}), body: JSON.stringify({
}) name: newOrgName,
.then(async res => { }),
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return res; return res;
} else { } else {
console.log('Failed to rename an organization'); console.log("Failed to rename an organization");
} }
}) });
}; };
export default renameOrg; export default renameOrg;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -6,29 +6,36 @@ import { PATH } from "../../../const";
* @param {*} param0 * @param {*} param0
* @returns * @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/", { return SecurityClient.fetchCall(PATH + "/api/v1/service-token/", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
name, name,
workspaceId, workspaceId,
environment, environment,
expiresIn, expiresIn,
publicKey, publicKey,
encryptedKey, encryptedKey,
nonce nonce,
}) }),
}) }).then(async (res) => {
.then(async res => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).token; return (await res.json()).token;
} else { } else {
console.log('Failed to add service tokens'); console.log("Failed to add service tokens");
} }
}) });
}; };
export default addServiceToken; export default addServiceToken;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -6,20 +6,22 @@ import { PATH } from "../../../const";
* @param {*} param0 * @param {*} param0
* @returns * @returns
*/ */
const getServiceTokens = ({workspaceId}) => { const getServiceTokens = ({ workspaceId }) => {
return SecurityClient.fetchCall(PATH + "/api/v1/workspace/" + workspaceId + "/service-tokens", { return SecurityClient.fetchCall(
method: "GET", PATH + "/api/v1/workspace/" + workspaceId + "/service-tokens",
headers: { {
"Content-Type": "application/json", method: "GET",
}, headers: {
}) "Content-Type": "application/json",
.then(async res => { },
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).serviceTokens; return (await res.json()).serviceTokens;
} else { } else {
console.log('Failed to get service tokens'); console.log("Failed to get service tokens");
} }
}) });
}; };
export default getServiceTokens; export default getServiceTokens;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -13,14 +13,13 @@ const getUser = (req, res) => {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}) }).then(async (res) => {
.then(async res => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).user; return (await res.json()).user;
} else { } else {
console.log('Failed to get user info'); console.log("Failed to get user info");
} }
}) });
}; };
export default getUser; export default getUser;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -8,23 +8,26 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const checkUserAction = ({ action }) => { const checkUserAction = ({ action }) => {
return SecurityClient.fetchCall(PATH + "/api/v1/user-action" + return SecurityClient.fetchCall(
"?" + PATH +
new URLSearchParams({ "/api/v1/user-action" +
action, "?" +
}), { new URLSearchParams({
method: "GET", action,
headers: { }),
"Content-Type": "application/json", {
}, method: "GET",
}) headers: {
.then(async res => { "Content-Type": "application/json",
},
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).userAction; return (await res.json()).userAction;
} else { } else {
console.log('Failed to check a user action'); console.log("Failed to check a user action");
} }
}) });
}; };
export default checkUserAction; export default checkUserAction;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -15,14 +15,13 @@ const registerUserAction = ({ action }) => {
body: JSON.stringify({ body: JSON.stringify({
action, action,
}), }),
}) }).then(async (res) => {
.then(async res => {
if (res.status == 200) { if (res.status == 200) {
return res; return res;
} else { } else {
console.log('Failed to register a user action'); console.log("Failed to register a user action");
} }
}) });
}; };
export default registerUserAction; export default registerUserAction;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -8,22 +8,24 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const addUserToWorkspace = (email, workspaceId) => { const addUserToWorkspace = (email, workspaceId) => {
return SecurityClient.fetchCall(PATH + "/api/v1/workspace/" + workspaceId + "/invite-signup", { return SecurityClient.fetchCall(
method: "POST", PATH + "/api/v1/workspace/" + workspaceId + "/invite-signup",
headers: { {
"Content-Type": "application/json", method: "POST",
}, headers: {
body: JSON.stringify({ "Content-Type": "application/json",
email: email, },
}), 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');
} }
}) ).then(async (res) => {
if (res.status == 200) {
return await res.json();
} else {
console.log("Failed to add a user to project");
}
});
}; };
export default addUserToWorkspace; export default addUserToWorkspace;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -8,22 +8,24 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const changeUserRoleInWorkspace = (membershipId, role) => { const changeUserRoleInWorkspace = (membershipId, role) => {
return SecurityClient.fetchCall(PATH + "/api/v1/membership/" + membershipId + "/change-role", { return SecurityClient.fetchCall(
method: "POST", PATH + "/api/v1/membership/" + membershipId + "/change-role",
headers: { {
"Content-Type": "application/json", method: "POST",
}, headers: {
body: JSON.stringify({ "Content-Type": "application/json",
role: role, },
}), body: JSON.stringify({
}) role: role,
.then(async res => { }),
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return res; return res;
} else { } 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; export default changeUserRoleInWorkspace;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -16,14 +16,13 @@ const createWorkspace = (workspaceName, organizationId) => {
workspaceName: workspaceName, workspaceName: workspaceName,
organizationId: organizationId, organizationId: organizationId,
}), }),
}) }).then(async (res) => {
.then(async res => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).workspace; return (await res.json()).workspace;
} else { } else {
console.log('Failed to create a project'); console.log("Failed to create a project");
} }
}) });
}; };
export default createWorkspace; export default createWorkspace;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -7,19 +7,21 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const deleteUserFromWorkspace = (membershipId) => { const deleteUserFromWorkspace = (membershipId) => {
return SecurityClient.fetchCall(PATH + "/api/v1/membership/" + membershipId, { return SecurityClient.fetchCall(
method: "DELETE", PATH + "/api/v1/membership/" + membershipId,
headers: { {
"Content-Type": "application/json", method: "DELETE",
}, headers: {
}) "Content-Type": "application/json",
.then(async res => { },
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return res; return res;
} else { } else {
console.log('Failed to delete a user from a project'); console.log("Failed to delete a user from a project");
} }
}) });
}; };
export default deleteUserFromWorkspace; export default deleteUserFromWorkspace;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -12,14 +12,13 @@ const deleteWorkspace = (workspaceId) => {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}) }).then(async (res) => {
.then(async res => {
if (res.status == 200) { if (res.status == 200) {
return res; return res;
} else { } else {
console.log('Failed to delete a project'); console.log("Failed to delete a project");
} }
}) });
}; };
export default deleteWorkspace; export default deleteWorkspace;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -7,19 +7,23 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const getLatestFileKey = (workspaceId) => { const getLatestFileKey = (workspaceId) => {
return SecurityClient.fetchCall(PATH + "/api/v1/key/" + workspaceId + "/latest", { return SecurityClient.fetchCall(
method: "GET", PATH + "/api/v1/key/" + workspaceId + "/latest",
headers: { {
"Content-Type": "application/json", 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');
} }
}) ).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; export default getLatestFileKey;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -8,19 +8,21 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const getWorkspaceInfo = (req, res) => { const getWorkspaceInfo = (req, res) => {
return SecurityClient.fetchCall(PATH + "/api/v1/workspace/" + req.workspaceId, { return SecurityClient.fetchCall(
method: "GET", PATH + "/api/v1/workspace/" + req.workspaceId,
headers: { {
"Content-Type": "application/json", method: "GET",
}, headers: {
}) "Content-Type": "application/json",
.then(async res => { },
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).workspace; return (await res.json()).workspace;
} else { } else {
console.log('Failed to get project info'); console.log("Failed to get project info");
} }
}) });
}; };
export default getWorkspaceInfo; export default getWorkspaceInfo;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -8,19 +8,23 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const getWorkspaceKeys = (req, res) => { const getWorkspaceKeys = (req, res) => {
return SecurityClient.fetchCall(PATH + "/api/v1/workspace/" + req.workspaceId + "/keys", { return SecurityClient.fetchCall(
method: "GET", PATH + "/api/v1/workspace/" + req.workspaceId + "/keys",
headers: { {
"Content-Type": "application/json", method: "GET",
}, headers: {
}) "Content-Type": "application/json",
.then(async res => { },
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).publicKeys; return (await res.json()).publicKeys;
} else { } 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; export default getWorkspaceKeys;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -8,19 +8,21 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const getWorkspaceUsers = (req, res) => { const getWorkspaceUsers = (req, res) => {
return SecurityClient.fetchCall(PATH + "/api/v1/workspace/" + req.workspaceId + "/users", { return SecurityClient.fetchCall(
method: "GET", PATH + "/api/v1/workspace/" + req.workspaceId + "/users",
headers: { {
"Content-Type": "application/json", method: "GET",
}, headers: {
}) "Content-Type": "application/json",
.then(async res => { },
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).users; return (await res.json()).users;
} else { } else {
console.log('Failed to get Project Users'); console.log("Failed to get Project Users");
} }
}) });
}; };
export default getWorkspaceUsers; export default getWorkspaceUsers;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -13,14 +13,13 @@ const getWorkspaces = (req, res) => {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}) }).then(async (res) => {
.then(async res => {
if (res.status == 200) { if (res.status == 200) {
return (await res.json()).workspaces; return (await res.json()).workspaces;
} else { } else {
console.log('Failed to get projects'); console.log("Failed to get projects");
} }
}) });
}; };
export default getWorkspaces; export default getWorkspaces;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -8,22 +8,24 @@ import { PATH } from "../../../const";
* @returns * @returns
*/ */
const renameWorkspace = (workspaceId, newWorkspaceName) => { const renameWorkspace = (workspaceId, newWorkspaceName) => {
return SecurityClient.fetchCall(PATH + "/api/v1/workspace/" + workspaceId + "/name", { return SecurityClient.fetchCall(
method: "POST", PATH + "/api/v1/workspace/" + workspaceId + "/name",
headers: { {
"Content-Type": "application/json", method: "POST",
}, headers: {
body: JSON.stringify({ "Content-Type": "application/json",
name: newWorkspaceName, },
}), body: JSON.stringify({
}) name: newWorkspaceName,
.then(async res => { }),
}
).then(async (res) => {
if (res.status == 200) { if (res.status == 200) {
return res; return res;
} else { } else {
console.log('Failed to rename a project'); console.log("Failed to rename a project");
} }
}) });
}; };
export default renameWorkspace; export default renameWorkspace;

View File

@@ -1,4 +1,4 @@
import SecurityClient from "../../../components/utilities/SecurityClient"; import SecurityClient from "~/utilities/SecurityClient";
import { PATH } from "../../../const"; import { PATH } from "../../../const";
/** /**
@@ -22,14 +22,13 @@ const uploadKeys = (workspaceId, userId, encryptedKey, nonce) => {
nonce: nonce, nonce: nonce,
}, },
}), }),
}) }).then(async (res) => {
.then(async res => {
if (res.status == 200) { if (res.status == 200) {
return res; return res;
} else { } else {
console.log('Failed to upload keys for a new user'); console.log("Failed to upload keys for a new user");
} }
}) });
}; };
export default uploadKeys; export default uploadKeys;

View File

@@ -2,14 +2,14 @@ import React, { useState, useEffect, useCallback, Fragment } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import Head from "next/head"; import Head from "next/head";
import Image from "next/image"; import Image from "next/image";
import guidGenerator from "../../components/utilities/randomId"; import guidGenerator from "~/utilities/randomId";
import getSecretsForProject from "../../components/utilities/getSecretsForProject"; import getSecretsForProject from "~/utilities/getSecretsForProject";
import pushKeys from "../../components/utilities/pushKeys"; import pushKeys from "~/utilities/pushKeys";
import getWorkspaces from "../api/workspace/getWorkspaces"; import getWorkspaces from "../api/workspace/getWorkspaces";
import getUser from "../api/user/getUser"; import getUser from "../api/user/getUser";
import NavHeader from "../../components/navigation/NavHeader"; import NavHeader from "~/components/navigation/NavHeader";
import DashboardInputField from "../../components/dashboard/DashboardInputField"; import DashboardInputField from "~/components/dashboard/DashboardInputField";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { import {
faMagnifyingGlass, faMagnifyingGlass,
@@ -26,19 +26,26 @@ import {
faCheck, faCheck,
faCopy, faCopy,
faCircleInfo, faCircleInfo,
faX faX,
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
import ListBox from "../../components/basic/Listbox"; import ListBox from "~/components/basic/Listbox";
import DropZone from "../../components/dashboard/DropZone"; import DropZone from "~/components/dashboard/DropZone";
import { Menu, Transition } from "@headlessui/react"; import { Menu, Transition } from "@headlessui/react";
import getWorkspaceIntegrations from "../api/integrations/getWorkspaceIntegrations"; import getWorkspaceIntegrations from "../api/integrations/getWorkspaceIntegrations";
import BottonRightPopup from "../../components/basic/popups/BottomRightPopup"; import BottonRightPopup from "~/components/basic/popups/BottomRightPopup";
import checkUserAction from "../api/userActions/checkUserAction"; import checkUserAction from "../api/userActions/checkUserAction";
import registerUserAction from "../api/userActions/registerUserAction"; import registerUserAction from "../api/userActions/registerUserAction";
import pushKeysIntegration from "../../components/utilities/pushKeysIntegration"; import pushKeysIntegration from "~/utilities/pushKeysIntegration";
import Button from "../../components/basic/buttons/Button"; import Button from "~/components/basic/buttons/Button";
const KeyPair = ({ keyPair, deleteRow, modifyKey, modifyValue, modifyVisibility, isBlurred }) => { const KeyPair = ({
keyPair,
deleteRow,
modifyKey,
modifyValue,
modifyVisibility,
isBlurred,
}) => {
return ( return (
<div className="px-1 flex flex-col items-center ml-1"> <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"> <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 +93,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"> <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 <div
onClick={() => onClick={() =>
modifyVisibility(keyPair[4] == "personal" modifyVisibility(
? "shared" keyPair[4] == "personal"
: "personal", keyPair[1]) ? "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" 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 +133,6 @@ const KeyPair = ({ keyPair, deleteRow, modifyKey, modifyValue, modifyVisibility,
); );
}; };
const envMapping = { const envMapping = {
Development: "dev", Development: "dev",
Staging: "staging", Staging: "staging",
@@ -186,7 +195,6 @@ export default function Dashboard() {
}; };
}, [buttonReady]); }, [buttonReady]);
const reorderRows = () => { const reorderRows = () => {
setSortMethod( setSortMethod(
sortMethod == "alphabetical" ? "-alphabetical" : "alphabetical" sortMethod == "alphabetical" ? "-alphabetical" : "alphabetical"
@@ -218,21 +226,21 @@ export default function Dashboard() {
setFileState, setFileState,
setIsKeyAvailable, setIsKeyAvailable,
setData, setData,
workspaceId: router.query.id workspaceId: router.query.id,
}) });
const user = await getUser(); const user = await getUser();
setIsNew( setIsNew(
(Date.parse(new Date()) - (Date.parse(new Date()) - Date.parse(user.createdAt)) / 60000 <
Date.parse(user.createdAt)) /
60000 <
3 3
? true ? true
: false : false
); );
let userAction = await checkUserAction({action: "first_time_secrets_pushed"}); let userAction = await checkUserAction({
setHasUserEverPushed(userAction ? true : false) action: "first_time_secrets_pushed",
});
setHasUserEverPushed(userAction ? true : false);
} catch (error) { } catch (error) {
console.log("Error", error); console.log("Error", error);
setData([]); setData([]);
@@ -252,39 +260,38 @@ export default function Dashboard() {
const modifyValue = (value, id) => { const modifyValue = (value, id) => {
setData((oldData) => { setData((oldData) => {
oldData[id][3] = value; oldData[id][3] = value;
return [...oldData] return [...oldData];
}); });
setButtonReady(true); setButtonReady(true);
} };
const modifyKey = (value, id) => { const modifyKey = (value, id) => {
setData((oldData) => { setData((oldData) => {
oldData[id][2] = value; oldData[id][2] = value;
return [...oldData] return [...oldData];
}); });
setButtonReady(true); setButtonReady(true);
} };
const modifyVisibility = (value, id) => { const modifyVisibility = (value, id) => {
setData((oldData) => { setData((oldData) => {
oldData[id][4] = value; oldData[id][4] = value;
return [...oldData] return [...oldData];
}); });
setButtonReady(true); setButtonReady(true);
} };
const listenChangeValue = useCallback((value, id) => { const listenChangeValue = useCallback((value, id) => {
modifyValue(value, id) modifyValue(value, id);
}, []) }, []);
const listenChangeKey = useCallback((value, id) => { const listenChangeKey = useCallback((value, id) => {
modifyKey(value, id) modifyKey(value, id);
}, []) }, []);
const listenChangeVisibility = useCallback((value, id) => { const listenChangeVisibility = useCallback((value, id) => {
modifyVisibility(value, id) modifyVisibility(value, id);
}, []) }, []);
const savePush = async () => { const savePush = async () => {
let obj = Object.assign( let obj = Object.assign(
@@ -294,19 +301,28 @@ export default function Dashboard() {
setButtonReady(false); setButtonReady(false);
pushKeys(obj, router.query.id, env); 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) => { integrations.map(async (integration) => {
if (envMapping[env] == integration.environment && integration.isActive == true) { if (
envMapping[env] == integration.environment &&
integration.isActive == true
) {
let objIntegration = Object.assign( let objIntegration = Object.assign(
{}, {},
...data.map((row) => ({ [row[2]]: row[3] })) ...data.map((row) => ({ [row[2]]: row[3] }))
); );
await pushKeysIntegration({obj: objIntegration, integrationId: integration._id});} await pushKeysIntegration({
obj: objIntegration,
integrationId: integration._id,
});
}
}); });
if (!hasUserEverPushed) { if (!hasUserEverPushed) {
setCheckDocsPopUpVisible(true) setCheckDocsPopUpVisible(true);
await registerUserAction({action: "first_time_secrets_pushed"}); await registerUserAction({ action: "first_time_secrets_pushed" });
} }
}; };
@@ -339,20 +355,19 @@ export default function Dashboard() {
function copyToClipboard() { function copyToClipboard() {
// Get the text field // Get the text field
var copyText = document.getElementById("myInput"); var copyText = document.getElementById("myInput");
// Select the text field // Select the text field
copyText.select(); copyText.select();
copyText.setSelectionRange(0, 99999); // For mobile devices 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); navigator.clipboard.writeText(copyText.value);
setProjectIdCopied(true); setProjectIdCopied(true);
setTimeout(() => setProjectIdCopied(false), 2000); setTimeout(() => setProjectIdCopied(false), 2000);
// Alert the copied text // Alert the copied text
// alert("Copied the text: " + copyText.value); // alert("Copied the text: " + copyText.value);
} }
return data ? ( return data ? (
<div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white"> <div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white">
@@ -371,41 +386,61 @@ export default function Dashboard() {
</Head> </Head>
<div className="flex flex-row"> <div className="flex flex-row">
<div className="w-full max-h-96 pb-2"> <div className="w-full max-h-96 pb-2">
<NavHeader pageName="Secrets" isProjectRelated={true}/> <NavHeader pageName="Secrets" isProjectRelated={true} />
{checkDocsPopUpVisible && {checkDocsPopUpVisible && (
<BottonRightPopup <BottonRightPopup
buttonText="Check Docs" buttonText="Check Docs"
buttonLink="https://infisical.com/docs/getting-started/introduction" buttonLink="https://infisical.com/docs/getting-started/introduction"
titleText="Good job!" titleText="Good job!"
emoji="🎉" emoji="🎉"
textLine1="Congrats on adding more secrets." textLine1="Congrats on adding more secrets."
textLine2="Here is how to connect them to your codebase." textLine2="Here is how to connect them to your codebase."
setCheckDocsPopUpVisible={setCheckDocsPopUpVisible} 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-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"> <div className="flex flex-row justify-start items-center text-3xl">
<p className="font-semibold mr-4 mt-1">Secrets</p> <p className="font-semibold mr-4 mt-1">Secrets</p>
{data?.length==0 && <ListBox {data?.length == 0 && (
selected={env} <ListBox
data={[ selected={env}
"Development", data={[
"Staging", "Development",
"Production", "Staging",
"Testing", "Production",
]} "Testing",
// ref={useRef(123)} ]}
onChange={setEnv} // ref={useRef(123)}
className="z-40" onChange={setEnv}
/>} className="z-40"
/>
)}
</div> </div>
<div className="flex flex-row"> <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"> <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> <p className="mr-2 font-bold pl-4">
<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> 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"> <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"> <button
{projectIdCopied ? <FontAwesomeIcon icon={faCheck} className="pr-0.5"/> : <FontAwesomeIcon icon={faCopy} />} 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> </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"> <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 Click to Copy
@@ -455,7 +490,9 @@ export default function Dashboard() {
className="pl-2 text-gray-400 rounded-r-md bg-white/5 w-full h-full outline-none" className="pl-2 text-gray-400 rounded-r-md bg-white/5 w-full h-full outline-none"
value={searchKeys} value={searchKeys}
onChange={(e) => onChange={(e) =>
setSearchKeys(e.target.value) setSearchKeys(
e.target.value
)
} }
placeholder={"Search keys..."} placeholder={"Search keys..."}
/> />
@@ -465,7 +502,11 @@ export default function Dashboard() {
onButtonPressed={reorderRows} onButtonPressed={reorderRows}
color="mineshaft" color="mineshaft"
size="icon-md" size="icon-md"
icon={sortMethod == "alphabetical" ? faArrowDownAZ : faArrowDownZA} icon={
sortMethod == "alphabetical"
? faArrowDownAZ
: faArrowDownZA
}
/> />
</div> </div>
<div className="ml-2 min-w-max flex flex-row items-start justify-start"> <div className="ml-2 min-w-max flex flex-row items-start justify-start">
@@ -481,7 +522,9 @@ export default function Dashboard() {
onButtonPressed={changeBlurred} onButtonPressed={changeBlurred}
color="mineshaft" color="mineshaft"
size="icon-md" size="icon-md"
icon={blurred ? faEye : faEyeSlash} icon={
blurred ? faEye : faEyeSlash
}
/> />
</div> </div>
<div className="relative ml-2 min-w-max flex flex-row items-start justify-end"> <div className="relative ml-2 min-w-max flex flex-row items-start justify-end">
@@ -514,14 +557,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"> <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} /> */} {/* <FontAwesomeIcon icon={faAngleDown} /> */}
<div className="flex flex-row items-center"> <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"> <div className="group font-normal group relative inline-block text-gray-300 underline hover:text-primary duration-200">
<FontAwesomeIcon <FontAwesomeIcon
className="ml-3 mt-1 text-lg" className="ml-3 mt-1 text-lg"
icon={faCircleInfo} 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"> <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> </span>
</div> </div>
</div> </div>
@@ -534,7 +580,8 @@ export default function Dashboard() {
.toLowerCase() .toLowerCase()
.includes( .includes(
searchKeys.toLowerCase() searchKeys.toLowerCase()
) && keyPair[4] == "personal" ) &&
keyPair[4] == "personal"
) )
.sort((a, b) => .sort((a, b) =>
sortMethod == "alphabetical" sortMethod == "alphabetical"
@@ -546,9 +593,13 @@ export default function Dashboard() {
key={keyPair[0]} key={keyPair[0]}
keyPair={keyPair} keyPair={keyPair}
deleteRow={deleteCertainRow} deleteRow={deleteCertainRow}
modifyValue={listenChangeValue} modifyValue={
listenChangeValue
}
modifyKey={listenChangeKey} modifyKey={listenChangeKey}
modifyVisibility={listenChangeVisibility} modifyVisibility={
listenChangeVisibility
}
isBlurred={blurred} isBlurred={blurred}
/> />
))} ))}
@@ -562,15 +613,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"> <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} /> */} {/* <FontAwesomeIcon icon={faAngleDown} /> */}
<div className="flex flex-row items-center"> <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"> <div className="group font-normal group relative inline-block text-gray-300 underline hover:text-primary duration-200">
<FontAwesomeIcon <FontAwesomeIcon
className="ml-3 text-lg mt-1" className="ml-3 text-lg mt-1"
icon={faCircleInfo} 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"> <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 Shared keys are visible to
team your whole team
</span> </span>
</div> </div>
</div> </div>
@@ -583,7 +636,8 @@ export default function Dashboard() {
.toLowerCase() .toLowerCase()
.includes( .includes(
searchKeys.toLowerCase() searchKeys.toLowerCase()
) && keyPair[4] == "shared" ) &&
keyPair[4] == "shared"
) )
.sort((a, b) => .sort((a, b) =>
sortMethod == "alphabetical" sortMethod == "alphabetical"
@@ -595,9 +649,13 @@ export default function Dashboard() {
key={keyPair[0]} key={keyPair[0]}
keyPair={keyPair} keyPair={keyPair}
deleteRow={deleteCertainRow} deleteRow={deleteCertainRow}
modifyValue={listenChangeValue} modifyValue={
listenChangeValue
}
modifyKey={listenChangeKey} modifyKey={listenChangeKey}
modifyVisibility={listenChangeVisibility} modifyVisibility={
listenChangeVisibility
}
isBlurred={blurred} isBlurred={blurred}
/> />
))} ))}
@@ -606,7 +664,9 @@ export default function Dashboard() {
<div className="w-full max-w-5xl"> <div className="w-full max-w-5xl">
<DropZone <DropZone
setData={addData} setData={addData}
setErrorDragAndDrop={setErrorDragAndDrop} setErrorDragAndDrop={
setErrorDragAndDrop
}
createNewFile={addRow} createNewFile={addRow}
errorDragAndDrop={errorDragAndDrop} errorDragAndDrop={errorDragAndDrop}
setButtonReady={setButtonReady} setButtonReady={setButtonReady}
@@ -617,19 +677,23 @@ export default function Dashboard() {
</div> </div>
) : ( ) : (
<div className="flex flex-col items-center justify-center h-full text-xl text-gray-400 max-w-5xl mt-28"> <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 && ( fileState.message != undefined && (
<FontAwesomeIcon <FontAwesomeIcon
className="text-7xl mb-8" className="text-7xl mb-8"
icon={faFolderOpen} icon={faFolderOpen}
/> />
)} )}
{(fileState.message == "There's nothing to pull" || {(fileState.message ==
"There's nothing to pull" ||
fileState.message == undefined) && fileState.message == undefined) &&
isKeyAvailable && ( isKeyAvailable && (
<DropZone <DropZone
setData={setData} setData={setData}
setErrorDragAndDrop={setErrorDragAndDrop} setErrorDragAndDrop={
setErrorDragAndDrop
}
createNewFile={addRow} createNewFile={addRow}
errorDragAndDrop={errorDragAndDrop} errorDragAndDrop={errorDragAndDrop}
setButtonReady={setButtonReady} setButtonReady={setButtonReady}
@@ -638,7 +702,10 @@ export default function Dashboard() {
)} )}
{fileState.message == {fileState.message ==
"Failed membership validation for workspace" && ( "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 == {fileState.message ==
"Access needed to pull the latest file" || "Access needed to pull the latest file" ||
@@ -653,8 +720,8 @@ export default function Dashboard() {
administrator for permission. administrator for permission.
</p> </p>
<p className="mt-1"> <p className="mt-1">
They need to grant you access in the team They need to grant you access in
tab. the team tab.
</p> </p>
</> </>
))} ))}

View File

@@ -3,20 +3,25 @@ import { useRouter } from "next/router";
import Head from "next/head"; import Head from "next/head";
import Image from "next/image"; import Image from "next/image";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCheck, faArrowRight, faRotate, faX } from "@fortawesome/free-solid-svg-icons"; import {
import ListBox from "../../components/basic/Listbox"; faCheck,
import NavHeader from "../../components/navigation/NavHeader"; 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 getIntegrations from "../api/integrations/GetIntegrations";
import getIntegrationApps from "../api/integrations/GetIntegrationApps"; import getIntegrationApps from "../api/integrations/GetIntegrationApps";
import getWorkspaceAuthorizations from "../api/integrations/getWorkspaceAuthorizations"; import getWorkspaceAuthorizations from "../api/integrations/getWorkspaceAuthorizations";
import getWorkspaceIntegrations from "../api/integrations/getWorkspaceIntegrations"; import getWorkspaceIntegrations from "../api/integrations/getWorkspaceIntegrations";
import startIntegration from "../api/integrations/StartIntegration"; import startIntegration from "../api/integrations/StartIntegration";
import deleteIntegration from "../api/integrations/DeleteIntegration"; import deleteIntegration from "../api/integrations/DeleteIntegration";
import getSecretsForProject from "../../components/utilities/getSecretsForProject"; import getSecretsForProject from "~/utilities/getSecretsForProject";
import pushKeysIntegration from "../../components/utilities/pushKeysIntegration"; import pushKeysIntegration from "~/utilities/pushKeysIntegration";
import deleteIntegrationAuth from "../api/integrations/DeleteIntegrationAuth"; import deleteIntegrationAuth from "../api/integrations/DeleteIntegrationAuth";
import Button from "../../components/basic/buttons/Button"; import Button from "~/components/basic/buttons/Button";
import guidGenerator from "../../components/utilities/randomId"; import guidGenerator from "~/utilities/randomId";
const crypto = require("crypto"); const crypto = require("crypto");
@@ -28,104 +33,138 @@ const envMapping = {
}; };
const reverseEnvMapping = { const reverseEnvMapping = {
"dev": "Development", dev: "Development",
"staging": "Staging", staging: "Staging",
"prod": "Production", prod: "Production",
"test": "Testing" test: "Testing",
} };
const Integration = ({projectIntegration}) => { const Integration = ({ projectIntegration }) => {
const [integrationEnvironment, setIntegrationEnvironment] = useState(reverseEnvMapping[projectIntegration.environment]); const [integrationEnvironment, setIntegrationEnvironment] = useState(
reverseEnvMapping[projectIntegration.environment]
);
const [fileState, setFileState] = useState([]); const [fileState, setFileState] = useState([]);
const [data, setData] = useState(); const [data, setData] = useState();
const [isKeyAvailable, setIsKeyAvailable] = useState(true); const [isKeyAvailable, setIsKeyAvailable] = useState(true);
const router = useRouter(); const router = useRouter();
const [apps, setApps] = useState([]); 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 () => { useEffect(async () => {
const tempHerokuApps = await getIntegrationApps({integrationAuthId: projectIntegration.integrationAuth}); const tempHerokuApps = await getIntegrationApps({
const tempHerokuAppNames = tempHerokuApps.map(app => app.name); integrationAuthId: projectIntegration.integrationAuth,
});
const tempHerokuAppNames = tempHerokuApps.map((app) => app.name);
setApps(tempHerokuAppNames); setApps(tempHerokuAppNames);
setIntegrationApp(projectIntegration.app ? projectIntegration.app : tempHerokuAppNames[0]) setIntegrationApp(
projectIntegration.app
? projectIntegration.app
: tempHerokuAppNames[0]
);
}, []); }, []);
return (
return <div className="flex flex-col max-w-5xl justify-center bg-white/5 p-6 rounded-md mx-6 mt-8"> <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="relative px-4 flex flex-row items-center justify-between mb-4">
<div className="flex flex-row"> <div className="flex flex-row">
<div> <div>
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide"> <div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
ENVIRONMENT ENVIRONMENT
</div> </div>
<ListBox <ListBox
data={!projectIntegration.isActive && ["Development", "Staging", "Production"]} data={
selected={integrationEnvironment} !projectIntegration.isActive && [
onChange={setIntegrationEnvironment} "Development",
/> "Staging",
</div> "Production",
<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();
} }
}} selected={integrationEnvironment}
color="mineshaft" onChange={setIntegrationEnvironment}
size="md" />
</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"> <div className="opacity-50 hover:opacity-100 duration-200 ml-2">
<Button <Button
onButtonPressed={async () => { onButtonPressed={async () => {
await deleteIntegration({integrationId: projectIntegration._id}); await deleteIntegration({
integrationId: projectIntegration._id,
});
router.reload(); router.reload();
}} }}
color="red" color="red"
@@ -134,9 +173,10 @@ const Integration = ({projectIntegration}) => {
/> />
</div> </div>
</div> </div>
</div>
</div> </div>
</div>; );
} };
export default function Integrations() { export default function Integrations() {
const [integrations, setIntegrations] = useState(); const [integrations, setIntegrations] = useState();
@@ -150,12 +190,16 @@ export default function Integrations() {
setCsrfToken(tempCSRFToken); setCsrfToken(tempCSRFToken);
localStorage.setItem("latestCSRFToken", tempCSRFToken); localStorage.setItem("latestCSRFToken", tempCSRFToken);
let projectAuthorizations = await getWorkspaceAuthorizations({workspaceId: router.query.id}); let projectAuthorizations = await getWorkspaceAuthorizations({
workspaceId: router.query.id,
});
setAuthorizations(projectAuthorizations); setAuthorizations(projectAuthorizations);
const projectIntegrations = await getWorkspaceIntegrations({workspaceId: router.query.id}); const projectIntegrations = await getWorkspaceIntegrations({
workspaceId: router.query.id,
});
setProjectIntegrations(projectIntegrations); setProjectIntegrations(projectIntegrations);
try { try {
const integrationsData = await getIntegrations(); const integrationsData = await getIntegrations();
setIntegrations(integrationsData); setIntegrations(integrationsData);
@@ -181,7 +225,10 @@ export default function Integrations() {
</Head> </Head>
<div className="flex flex-row"> <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"> <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-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"> <div className="flex flex-row justify-start items-center text-3xl">
<p className="font-semibold mr-4"> <p className="font-semibold mr-4">
@@ -189,24 +236,31 @@ export default function Integrations() {
</p> </p>
</div> </div>
<p className="mr-4 text-base text-gray-400"> <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> </p>
</div> </div>
{projectIntegrations.length > 0 {projectIntegrations.length > 0 ? (
? projectIntegrations.map(projectIntegration => projectIntegrations.map((projectIntegration) => (
<Integration key={guidGenerator()} projectIntegration={projectIntegration} /> <Integration
) key={guidGenerator()}
: <div className="flex flex-col max-w-5xl justify-center bg-white/5 p-6 rounded-md mx-6 mt-8"> 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="relative px-4 flex flex-col text-gray-400 items-center justify-center">
<div className="mb-1"> <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>
<div className=""> <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>
</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-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"> <div className="flex flex-row justify-start items-center text-3xl">
<p className="font-semibold mr-4"> <p className="font-semibold mr-4">
@@ -214,50 +268,151 @@ export default function Integrations() {
</p> </p>
</div> </div>
<p className="mr-4 text-base text-gray-400"> <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>
<p className="mr-4 text-xs text-gray-600 mt-1"> <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> </p>
</div> </div>
<div className="grid gap-4 grid-cols-3 grid-rows-3 mx-6 mt-4 max-w-5xl"> <div className="grid gap-4 grid-cols-3 grid-rows-3 mx-6 mt-4 max-w-5xl">
{Object.keys(integrations).map(integration => {Object.keys(integrations).map((integration) => (
<div className={`relative ${["Heroku"].includes(integrations[integration].name) ? "" : "opacity-50"}`} key={integrations[integration].name} > <div
<a className={`relative ${
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}` : "#"}`} ["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" 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 <Image
src={`/images/integrations/${integrations[integration].name}.png`} src={`/images/integrations/${integrations[integration].name}.png`}
height={100} height={100}
width={100} width={100}
alt="integration logo" alt="integration logo"
></Image> ></Image>
{integrations[integration].name.split(" ").length > 2 {integrations[integration].name.split(" ")
? <div className="font-semibold text-gray-300 group-hover:text-gray-200 duration-200 text-3xl ml-8 max-w-xs"> .length > 2 ? (
<div>{integrations[integration].name.split(" ")[0]}</div> <div className="font-semibold text-gray-300 group-hover:text-gray-200 duration-200 text-3xl ml-8 max-w-xs">
<div className="text-base">{integrations[integration].name.split(" ")[1]}{" "}{integrations[integration].name.split(" ")[2]}</div> <div>
{
integrations[
integration
].name.split(" ")[0]
}
</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> <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> </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 className="absolute group z-50 top-0 right-0 flex flex-row">
<div onClick={() => { <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">
deleteIntegrationAuth({integrationAuthId: authorizations.filter(authorization => authorization.integration == integrations[integration].name.toLowerCase()).map(authorization => authorization._id)[0]}); Coming Soon
router.reload(); </div>
}} 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> </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>
</div> </div>
</div> </div>

View File

@@ -5,11 +5,11 @@ import Head from "next/head";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import InputField from "../components/basic/InputField"; import InputField from "~/components/basic/InputField";
import Error from "../components/basic/Error"; import Error from "~/components/basic/Error";
import Button from "../components/basic/buttons/Button"; import Button from "~/components/basic/buttons/Button";
import getWorkspaces from "./api/workspace/getWorkspaces"; import getWorkspaces from "./api/workspace/getWorkspaces";
import attemptLogin from "../components/utilities/attemptLogin"; import attemptLogin from "~/utilities/attemptLogin";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faWarning } from "@fortawesome/free-solid-svg-icons"; import { faWarning } from "@fortawesome/free-solid-svg-icons";
@@ -36,13 +36,18 @@ export default function Login() {
*/ */
const loginCheck = async () => { const loginCheck = async () => {
setIsLoading(true); setIsLoading(true);
await attemptLogin(email, password, setErrorLogin, router, false, true).then( await attemptLogin(
() => { email,
setTimeout(function () { password,
setIsLoading(false); setErrorLogin,
}, 2000); router,
} false,
); true
).then(() => {
setTimeout(function () {
setIsLoading(false);
}, 2000);
});
}; };
return ( 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> <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> */}
</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"> <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"/> <FontAwesomeIcon
We are experiencing minor technical difficulties. We are working on solving it right now. Please come back in a few minutes. icon={faWarning}
</div>} 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> </div>
); );
} }

View File

@@ -1,11 +1,10 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import Head from "next/head"; import Head from "next/head";
import Plan from "../../../components/billing/Plan"; import Plan from "~/components/billing/Plan";
import getOrganizationSubscriptions from "../../api/organization/GetOrgSubscription" import getOrganizationSubscriptions from "../../api/organization/GetOrgSubscription";
import getOrganizationUsers from "../../api/organization/GetOrgUsers" import getOrganizationUsers from "../../api/organization/GetOrgUsers";
import NavHeader from "../../../components/navigation/NavHeader"; import NavHeader from "~/components/navigation/NavHeader";
import { STRIPE_PRODUCT_PRO, STRIPE_PRODUCT_STARTER } from "../../../components/utilities/config"; import { STRIPE_PRODUCT_PRO, STRIPE_PRODUCT_STARTER } from "~/utilities/config";
export default function SettingsBilling() { export default function SettingsBilling() {
let [currentPlan, setCurrentPlan] = useState(""); let [currentPlan, setCurrentPlan] = useState("");
@@ -46,57 +45,62 @@ export default function SettingsBilling() {
]; ];
useEffect(async () => { useEffect(async () => {
const subscriptions = await getOrganizationSubscriptions({orgId: localStorage.getItem("orgData.id")}); const subscriptions = await getOrganizationSubscriptions({
setCurrentPlan(subscriptions.data[0].plan.product) orgId: localStorage.getItem("orgData.id"),
const orgUsers = await getOrganizationUsers({orgId: localStorage.getItem("orgData.id")}) });
setNumUsers(orgUsers.length) setCurrentPlan(subscriptions.data[0].plan.product);
const orgUsers = await getOrganizationUsers({
orgId: localStorage.getItem("orgData.id"),
});
setNumUsers(orgUsers.length);
}, []); }, []);
return ( return (
<div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white"> <div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white">
<Head> <Head>
<title>Settings - Billing</title> <title>Settings - Billing</title>
<link rel="icon" href="/infisical.ico" /> <link rel="icon" href="/infisical.ico" />
</Head> </Head>
<div className="flex flex-row"> <div className="flex flex-row">
<div className="w-full max-h-screen pb-2 overflow-y-auto"> <div className="w-full max-h-screen pb-2 overflow-y-auto">
<NavHeader pageName="Usage & Billing"/> <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-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"> <div className="flex flex-col justify-start items-start text-3xl">
<p className="font-semibold mr-4 text-gray-200"> <p className="font-semibold mr-4 text-gray-200">
Usage & Billing Usage & Billing
</p> </p>
<p className="font-normal mr-4 text-gray-400 text-base"> <p className="font-normal mr-4 text-gray-400 text-base">
View and manage your organization's subscription here. View and manage your organization's subscription
</p> here.
</div> </p>
</div> </div>
<div className="flex flex-col ml-6 text-mineshaft-50"> </div>
<p className="text-xl font-semibold">Subscription</p> <div className="flex flex-col ml-6 text-mineshaft-50">
<div className="flex flex-row mt-4 overflow-x-auto"> <p className="text-xl font-semibold">Subscription</p>
{plans.map((plan) => ( <div className="flex flex-row mt-4 overflow-x-auto">
<Plan {plans.map((plan) => (
key={plan.name} <Plan key={plan.name} plan={plan} />
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> </div>
<p className='text-xl font-bold mt-12'>Current Usage</p> {/* <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="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">
<p className="text-6xl font-bold">1 </p> <p className="text-6xl font-bold">1 </p>
<p className="text-gray-300">Organization projects</p> <p className="text-gray-300">Organization projects</p>
</div> */} </div> */}
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
); );
} }

View File

@@ -2,26 +2,29 @@ import React, { useState, useEffect } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import Head from "next/head"; import Head from "next/head";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faMagnifyingGlass, faPlus, faX } from "@fortawesome/free-solid-svg-icons"; import {
faMagnifyingGlass,
faPlus,
faX,
} from "@fortawesome/free-solid-svg-icons";
import { faCheck } from "@fortawesome/free-solid-svg-icons"; import { faCheck } from "@fortawesome/free-solid-svg-icons";
import InputField from "../../../components/basic/InputField"; import InputField from "~/components/basic/InputField";
import getWorkspaces from "../../api/workspace/getWorkspaces"; import getWorkspaces from "../../api/workspace/getWorkspaces";
import AddIncidentContactDialog from "../../../components/basic/dialog/AddIncidentContactDialog"; import AddIncidentContactDialog from "~/components/basic/dialog/AddIncidentContactDialog";
import getIncidentContacts from "../../api/organization/getIncidentContacts"; import getIncidentContacts from "../../api/organization/getIncidentContacts";
import deleteIncidentContact from "../../api/organization/deleteIncidentContact"; import deleteIncidentContact from "../../api/organization/deleteIncidentContact";
import deleteWorkspace from "../../api/workspace/deleteWorkspace"; import deleteWorkspace from "../../api/workspace/deleteWorkspace";
import AddUserDialog from "../../../components/basic/dialog/AddUserDialog"; import AddUserDialog from "~/components/basic/dialog/AddUserDialog";
import UserTable from "../../../components/basic/table/UserTable"; import UserTable from "~/components/basic/table/UserTable";
import getUser from "../../api/user/getUser"; import getUser from "../../api/user/getUser";
import guidGenerator from "../../../components/utilities/randomId"; import guidGenerator from "~/utilities/randomId";
import addUserToOrg from "../../api/organization/addUserToOrg"; import addUserToOrg from "../../api/organization/addUserToOrg";
import getOrganizationUsers from "../../api/organization/GetOrgUsers"; import getOrganizationUsers from "../../api/organization/GetOrgUsers";
import renameOrg from "../../api/organization/renameOrg"; import renameOrg from "../../api/organization/renameOrg";
import getOrganization from "../../api/organization/GetOrg"; import getOrganization from "../../api/organization/GetOrg";
import getOrganizationSubscriptions from "../../api/organization/GetOrgSubscription"; import getOrganizationSubscriptions from "../../api/organization/GetOrgSubscription";
import NavHeader from "../../../components/navigation/NavHeader"; import NavHeader from "~/components/navigation/NavHeader";
import Button from "../../../components/basic/buttons/Button"; import Button from "~/components/basic/buttons/Button";
export default function SettingsOrg() { export default function SettingsOrg() {
const [buttonReady, setButtonReady] = useState(false); const [buttonReady, setButtonReady] = useState(false);
@@ -51,12 +54,12 @@ export default function SettingsOrg() {
}); });
let orgData = org; let orgData = org;
setOrgName(orgData.name); setOrgName(orgData.name);
let incidentContactsData = await getIncidentContacts(localStorage.getItem("orgData.id")); let incidentContactsData = await getIncidentContacts(
localStorage.getItem("orgData.id")
);
setIncidentContacts( setIncidentContacts(
incidentContactsData?.map( incidentContactsData?.map((contact) => contact.email)
(contact) => contact.email
)
); );
const user = await getUser(); const user = await getUser();
@@ -82,8 +85,10 @@ export default function SettingsOrg() {
publicKey: user.user?.publicKey, publicKey: user.user?.publicKey,
})) }))
); );
const subscriptions = await getOrganizationSubscriptions({orgId: localStorage.getItem("orgData.id")}); const subscriptions = await getOrganizationSubscriptions({
setCurrentPlan(subscriptions.data[0].plan.product) orgId: localStorage.getItem("orgData.id"),
});
setCurrentPlan(subscriptions.data[0].plan.product);
}, []); }, []);
const modifyOrgName = (newName) => { const modifyOrgName = (newName) => {
@@ -127,7 +132,10 @@ export default function SettingsOrg() {
setIncidentContacts( setIncidentContacts(
incidentContacts.filter((contact) => contact != incidentContact) incidentContacts.filter((contact) => contact != incidentContact)
); );
deleteIncidentContact(localStorage.getItem("orgData.id"), incidentContact); deleteIncidentContact(
localStorage.getItem("orgData.id"),
incidentContact
);
}; };
/** /**
@@ -160,7 +168,7 @@ export default function SettingsOrg() {
</Head> </Head>
<div className="flex flex-row"> <div className="flex flex-row">
<div className="w-full max-h-screen pb-2 overflow-y-auto"> <div className="w-full max-h-screen pb-2 overflow-y-auto">
<NavHeader pageName="Organization Settings"/> <NavHeader pageName="Organization Settings" />
<AddIncidentContactDialog <AddIncidentContactDialog
isOpen={isAddIncidentContactOpen} isOpen={isAddIncidentContactOpen}
closeModal={closeAddIncidentContactModal} closeModal={closeAddIncidentContactModal}
@@ -201,7 +209,9 @@ export default function SettingsOrg() {
> >
<Button <Button
text="Save Changes" text="Save Changes"
onButtonPressed={() => submitChanges(orgName)} onButtonPressed={() =>
submitChanges(orgName)
}
color="mineshaft" color="mineshaft"
size="md" size="md"
active={buttonReady} active={buttonReady}
@@ -289,7 +299,9 @@ export default function SettingsOrg() {
<div className="mt-4 mb-2 min-w-max flex flex-row items-end justify-end justify-center"> <div className="mt-4 mb-2 min-w-max flex flex-row items-end justify-end justify-center">
<Button <Button
text="Add Contact" text="Add Contact"
onButtonPressed={openAddIncidentContactModal} onButtonPressed={
openAddIncidentContactModal
}
color="mineshaft" color="mineshaft"
size="md" size="md"
icon={faPlus} icon={faPlus}
@@ -318,7 +330,10 @@ export default function SettingsOrg() {
email.includes(searchIncidentContact) email.includes(searchIncidentContact)
) )
.map((contact) => ( .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"> <p className="text-gray-300">
{contact} {contact}
</p> </p>

View File

@@ -2,14 +2,13 @@ import React, { useState, useEffect } from "react";
import Head from "next/head"; import Head from "next/head";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCheck, faX } from "@fortawesome/free-solid-svg-icons"; import { faCheck, faX } from "@fortawesome/free-solid-svg-icons";
import InputField from "../../../components/basic/InputField"; import InputField from "~/components/basic/InputField";
import getUser from "../../api/user/getUser"; import getUser from "../../api/user/getUser";
import NavHeader from "../../../components/navigation/NavHeader"; import NavHeader from "~/components/navigation/NavHeader";
import passwordCheck from "../../../components/utilities/checks/PasswordCheck"; import passwordCheck from "~/utilities/checks/PasswordCheck";
import changePassword from "../../../components/utilities/changePassword"; import changePassword from "~/utilities/changePassword";
import issueBackupKey from "../../../components/utilities/issueBackupKey"; import issueBackupKey from "~/utilities/issueBackupKey";
import Button from "../../../components/basic/buttons/Button"; import Button from "~/components/basic/buttons/Button";
export default function PersonalSettings() { export default function PersonalSettings() {
const [personalEmail, setPersonalEmail] = useState(""); const [personalEmail, setPersonalEmail] = useState("");
@@ -31,8 +30,6 @@ export default function PersonalSettings() {
setPersonalName(user.firstName + " " + user.lastName); setPersonalName(user.firstName + " " + user.lastName);
}, []); }, []);
return ( return (
<div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white"> <div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white">
<Head> <Head>
@@ -41,7 +38,10 @@ export default function PersonalSettings() {
</Head> </Head>
<div className="flex flex-row"> <div className="flex flex-row">
<div className="w-full max-h-screen pb-2 overflow-y-auto"> <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-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"> <div className="flex flex-col justify-start items-start text-3xl">
<p className="font-semibold mr-4 text-gray-200"> <p className="font-semibold mr-4 text-gray-200">
@@ -137,52 +137,133 @@ export default function PersonalSettings() {
type="password" type="password"
value={newPassword} value={newPassword}
isRequired isRequired
error={passwordErrorLength && passwordErrorLowerCase && passwordErrorNumber} error={
passwordErrorLength &&
passwordErrorLowerCase &&
passwordErrorNumber
}
/> />
</div> </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"> {passwordErrorLength ||
<div className={`text-gray-400 text-sm mb-1`}>Password should contain at least:</div> passwordErrorLowerCase ||
<div className="flex flex-row justify-start items-center ml-1"> passwordErrorNumber ? (
{passwordErrorLength <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">
? <FontAwesomeIcon icon={faX} className="text-md text-red mr-2.5"/> <div
: <FontAwesomeIcon icon={faCheck} className="text-md text-primary mr-2"/>} className={`text-gray-400 text-sm mb-1`}
<div className={`${passwordErrorLength ? "text-gray-400" : "text-gray-600"} text-sm`}>14 characters</div> >
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>
<div className="flex flex-row justify-start items-center ml-1"> ) : (
{passwordErrorLowerCase <div className="py-2"></div>
? <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="flex flex-row items-center mt-3 w-52 pr-3"> <div className="flex flex-row items-center mt-3 w-52 pr-3">
<Button <Button
text="Change Password" text="Change Password"
onButtonPressed={() => { onButtonPressed={() => {
if (!passwordErrorLength && !passwordErrorLowerCase && !passwordErrorNumber) { if (
!passwordErrorLength &&
!passwordErrorLowerCase &&
!passwordErrorNumber
) {
changePassword( changePassword(
personalEmail, personalEmail,
currentPassword, currentPassword,
newPassword, newPassword,
setCurrentPasswordError, setCurrentPasswordError,
setPasswordChanged, setPasswordChanged,
setCurrentPassword, setCurrentPassword,
setNewPassword setNewPassword
) );
} }
}} }}
color="mineshaft" color="mineshaft"
size="md" size="md"
active={(newPassword != "") && (currentPassword != "") && !(passwordErrorLength || passwordErrorLowerCase || passwordErrorNumber)} active={
newPassword != "" &&
currentPassword != "" &&
!(
passwordErrorLength ||
passwordErrorLowerCase ||
passwordErrorNumber
)
}
textDisabled="Change Password" 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>
</div> </div>
@@ -193,10 +274,14 @@ export default function PersonalSettings() {
Emergency Kit Emergency Kit
</p> </p>
<p className="text-sm text-mineshaft-300 min-w-max"> <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>
<p className="text-sm text-mineshaft-300 mb-5 min-w-max"> <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> </p>
</div> </div>
</div> </div>
@@ -216,11 +301,11 @@ export default function PersonalSettings() {
text="Download Emergency Kit" text="Download Emergency Kit"
onButtonPressed={() => { onButtonPressed={() => {
issueBackupKey({ issueBackupKey({
email: personalEmail, email: personalEmail,
password: backupPassword, password: backupPassword,
personalName, personalName,
setBackupKeyError, setBackupKeyError,
setBackupKeyIssued setBackupKeyIssued,
}); });
}} }}
color="mineshaft" color="mineshaft"
@@ -228,7 +313,14 @@ export default function PersonalSettings() {
active={backupPassword != ""} active={backupPassword != ""}
textDisabled="Download Emergency Kit" 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> </div>
</div> </div>

View File

@@ -3,15 +3,15 @@ import { useRouter } from "next/router";
import Head from "next/head"; import Head from "next/head";
import { faCheck, faPlus } from "@fortawesome/free-solid-svg-icons"; import { faCheck, faPlus } from "@fortawesome/free-solid-svg-icons";
import InputField from "../../../components/basic/InputField"; import InputField from "~/components/basic/InputField";
import getWorkspaces from "../../api/workspace/getWorkspaces"; import getWorkspaces from "../../api/workspace/getWorkspaces";
import renameWorkspace from "../../api/workspace/renameWorkspace"; import renameWorkspace from "../../api/workspace/renameWorkspace";
import deleteWorkspace from "../../api/workspace/deleteWorkspace"; import deleteWorkspace from "../../api/workspace/deleteWorkspace";
import NavHeader from "../../../components/navigation/NavHeader"; import NavHeader from "~/components/navigation/NavHeader";
import Button from "../../../components/basic/buttons/Button"; import Button from "~/components/basic/buttons/Button";
import ServiceTokenTable from "../../../components/basic/table/ServiceTokenTable"; import ServiceTokenTable from "~/components/basic/table/ServiceTokenTable";
import getServiceTokens from "../../api/serviceToken/getServiceTokens" import getServiceTokens from "../../api/serviceToken/getServiceTokens";
import AddServiceTokenDialog from "../../../components/basic/dialog/AddServiceTokenDialog"; import AddServiceTokenDialog from "~/components/basic/dialog/AddServiceTokenDialog";
export default function SettingsBasic() { export default function SettingsBasic() {
const [buttonReady, setButtonReady] = useState(false); const [buttonReady, setButtonReady] = useState(false);
@@ -22,7 +22,8 @@ export default function SettingsBasic() {
useState(""); useState("");
const [workspaceId, setWorkspaceId] = useState(""); const [workspaceId, setWorkspaceId] = useState("");
const [isAddOpen, setIsAddOpen] = useState(false); const [isAddOpen, setIsAddOpen] = useState(false);
let [isAddServiceTokenDialogOpen, setIsAddServiceTokenDialogOpen] = useState(false); let [isAddServiceTokenDialogOpen, setIsAddServiceTokenDialogOpen] =
useState(false);
useEffect(async () => { useEffect(async () => {
let userWorkspaces = await getWorkspaces(); let userWorkspaces = await getWorkspaces();
@@ -31,7 +32,9 @@ export default function SettingsBasic() {
setWorkspaceName(userWorkspace.name); setWorkspaceName(userWorkspace.name);
} }
}); });
let tempServiceTokens = await getServiceTokens({workspaceId: router.query.id}); let tempServiceTokens = await getServiceTokens({
workspaceId: router.query.id,
});
setServiceTokens(tempServiceTokens); setServiceTokens(tempServiceTokens);
}, []); }, []);
@@ -59,8 +62,8 @@ export default function SettingsBasic() {
const closeAddServiceTokenModal = () => { const closeAddServiceTokenModal = () => {
setIsAddServiceTokenDialogOpen(false); setIsAddServiceTokenDialogOpen(false);
} };
/** /**
* This function deleted a workspace. * This function deleted a workspace.
* It first checks if there is more than one workspace aviable. Otherwise, it doesn't delete * It first checks if there is more than one workspace aviable. Otherwise, it doesn't delete
@@ -97,7 +100,10 @@ export default function SettingsBasic() {
/> />
<div className="flex flex-row mr-6 max-w-5xl"> <div className="flex flex-row mr-6 max-w-5xl">
<div className="w-full max-h-screen pb-2 overflow-y-auto"> <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-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"> <div className="flex flex-col justify-start items-start text-3xl">
<p className="font-semibold mr-4 text-gray-200"> <p className="font-semibold mr-4 text-gray-200">
@@ -133,7 +139,9 @@ export default function SettingsBasic() {
> >
<Button <Button
text="Save Changes" text="Save Changes"
onButtonPressed={() => submitChanges(workspaceName)} onButtonPressed={() =>
submitChanges(workspaceName)
}
color="mineshaft" color="mineshaft"
size="md" size="md"
active={buttonReady} active={buttonReady}
@@ -184,20 +192,30 @@ export default function SettingsBasic() {
Service Tokens Service Tokens
</p> </p>
<p className="text-base text-gray-400 mb-4"> <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> </p>
</div> </div>
<div className="w-48"> <div className="w-48">
<Button <Button
text="Add New Token" text="Add New Token"
onButtonPressed={() => {setIsAddServiceTokenDialogOpen(true)}} onButtonPressed={() => {
setIsAddServiceTokenDialogOpen(
true
);
}}
color="mineshaft" color="mineshaft"
icon={faPlus} icon={faPlus}
size="md" size="md"
/> />
</div> </div>
</div> </div>
<ServiceTokenTable data={serviceTokens} workspaceName={workspaceName}/> <ServiceTokenTable
data={serviceTokens}
workspaceName={workspaceName}
/>
</div> </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"> {/* <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">

View File

@@ -6,19 +6,19 @@ import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import InputField from "../components/basic/InputField"; import InputField from "~/compoennts/basic/InputField";
import Error from "../components/basic/Error"; import Error from "~/compoennts/basic/Error";
import Button from "../components/basic/buttons/Button"; import Button from "~/compoennts/basic/buttons/Button";
import sendVerificationEmail from "./api/auth/SendVerificationEmail"; import sendVerificationEmail from "./api/auth/SendVerificationEmail";
import checkEmailVerificationCode from "./api/auth/CheckEmailVerificationCode"; import checkEmailVerificationCode from "./api/auth/CheckEmailVerificationCode";
import completeAccountInformationSignup from "./api/auth/CompleteAccountInformationSignup"; import completeAccountInformationSignup from "./api/auth/CompleteAccountInformationSignup";
import Aes256Gcm from "../components/aes-256-gcm"; import Aes256Gcm from "~/compoennts/aes-256-gcm";
import passwordCheck from "../components/utilities/checks/PasswordCheck"; import passwordCheck from "~/compoennts/utilities/checks/PasswordCheck";
import getWorkspaces from "./api/workspace/getWorkspaces"; import getWorkspaces from "./api/workspace/getWorkspaces";
import attemptLogin from "../components/utilities/attemptLogin"; import attemptLogin from "~/compoennts/utilities/attemptLogin";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCheck, faX, faWarning } from "@fortawesome/free-solid-svg-icons"; import { faCheck, faX, faWarning } from "@fortawesome/free-solid-svg-icons";
import issueBackupKey from "../components/utilities/issueBackupKey"; import issueBackupKey from "~/compoennts/utilities/issueBackupKey";
const ReactCodeInput = dynamic(import("react-code-input")); const ReactCodeInput = dynamic(import("react-code-input"));
const nacl = require("tweetnacl"); const nacl = require("tweetnacl");
@@ -73,7 +73,8 @@ export default function SignUp() {
const [passwordErrorNumber, setPasswordErrorNumber] = useState(false); const [passwordErrorNumber, setPasswordErrorNumber] = useState(false);
const [passwordErrorUpperCase, setPasswordErrorUpperCase] = useState(false); const [passwordErrorUpperCase, setPasswordErrorUpperCase] = useState(false);
const [passwordErrorLowerCase, setPasswordErrorLowerCase] = useState(false); const [passwordErrorLowerCase, setPasswordErrorLowerCase] = useState(false);
const [passwordErrorSpecialChar, setPasswordErrorSpecialChar] = useState(false); const [passwordErrorSpecialChar, setPasswordErrorSpecialChar] =
useState(false);
const [emailError, setEmailError] = useState(false); const [emailError, setEmailError] = useState(false);
const [emailErrorMessage, setEmailErrorMessage] = useState(""); const [emailErrorMessage, setEmailErrorMessage] = useState("");
const [step, setStep] = useState(1); const [step, setStep] = useState(1);
@@ -108,7 +109,7 @@ export default function SignUp() {
// Checking if the code matches the email. // Checking if the code matches the email.
const response = await checkEmailVerificationCode(email, code); const response = await checkEmailVerificationCode(email, code);
if (response.status == "200" || code == "111222") { if (response.status == "200" || code == "111222") {
setVerificationToken((await response.json()).token) setVerificationToken((await response.json()).token);
setStep(3); setStep(3);
} else { } else {
setCodeError(true); setCodeError(true);
@@ -180,7 +181,14 @@ export default function SignUp() {
const { ciphertext, iv, tag } = Aes256Gcm.encrypt( const { ciphertext, iv, tag } = Aes256Gcm.encrypt(
PRIVATE_KEY, 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); localStorage.setItem("PRIVATE_KEY", PRIVATE_KEY);
@@ -191,19 +199,21 @@ export default function SignUp() {
}, },
async () => { async () => {
client.createVerifier(async (err, result) => { client.createVerifier(async (err, result) => {
const response = await completeAccountInformationSignup({ const response = await completeAccountInformationSignup(
email, {
firstName, email,
lastName, firstName,
organizationName: firstName + "'s organization", lastName,
publicKey: PUBLIC_KEY, organizationName: firstName + "'s organization",
ciphertext, publicKey: PUBLIC_KEY,
iv, ciphertext,
tag, iv,
salt: result.salt, tag,
verifier: result.verifier, salt: result.salt,
token: verificationToken verifier: result.verifier,
}); token: verificationToken,
}
);
// if everything works, go the main dashboard page. // if everything works, go the main dashboard page.
if (!errorCheck && response.status == "200") { if (!errorCheck && response.status == "200") {
@@ -276,7 +286,11 @@ export default function SignUp() {
and acknowledged the Privacy Policy. and acknowledged the Privacy Policy.
</p> </p>
<div className="text-l mt-6 m-2 md:m-8 px-8 py-1 text-lg"> <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> </div>
</div> </div>
@@ -313,7 +327,11 @@ export default function SignUp() {
<Error text="Oops. Your code is wrong. Please try again." /> <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"> <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>
<div className="flex flex-col items-center justify-center w-full max-h-24 max-w-md mx-auto pt-2"> <div className="flex flex-col items-center justify-center w-full max-h-24 max-w-md mx-auto pt-2">
{/* <Link href="/login"> {/* <Link href="/login">
@@ -374,37 +392,101 @@ export default function SignUp() {
type="password" type="password"
value={password} value={password}
isRequired 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"> {passwordErrorLength ||
<div className={`text-gray-400 text-sm mb-1`}>Password should contain at least:</div> passwordErrorLowerCase ||
<div className="flex flex-row justify-start items-center ml-1"> passwordErrorNumber ? (
{passwordErrorLength <div className="w-full mt-4 bg-white/5 px-2 flex flex-col items-start py-2 rounded-md">
? <FontAwesomeIcon icon={faX} className="text-md text-red mr-2.5"/> <div className={`text-gray-400 text-sm mb-1`}>
: <FontAwesomeIcon icon={faCheck} className="text-md text-primary mr-2"/>} Password should contain at least:
<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">
{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>
<div className="flex flex-row justify-start items-center ml-1"> ) : (
{passwordErrorLowerCase <div className="py-2"></div>
? <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> </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"> <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>
</div> </div>
); );
// Step 4 of the sign up process (download the emergency kit pdf) // Step 4 of the sign up process (download the emergency kit pdf)
const step4 = ( 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"> <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 Save your Emergency Kit
</p> </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 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>
<div className="mt-3">We recommend you download it and keep it somewhere safe.</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>
<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"> <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"/> <FontAwesomeIcon
It contains your Secret Key which we cannot access or recover for you if you lose it. 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>
<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"> <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 <Button
text="Download PDF" text="Download PDF"
onButtonPressed={async () => { onButtonPressed={async () => {
await issueBackupKey({ await issueBackupKey({
email, email,
password, password,
personalName: (firstName + " " + lastName), personalName: firstName + " " + lastName,
setBackupKeyError, setBackupKeyError,
setBackupKeyIssued setBackupKeyIssued,
}); });
router.push("/dashboard/"); router.push("/dashboard/");
}} }}
@@ -476,7 +567,13 @@ export default function SignUp() {
/> />
</div> </div>
</Link> </Link>
{step == 1 ? step1 : step == 2 ? step2 : step == 3 ? step3 : step4} {step == 1
? step1
: step == 2
? step2
: step == 3
? step3
: step4}
</div> </div>
</div> </div>
); );

View File

@@ -5,15 +5,15 @@ import Head from "next/head";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import InputField from "../components/basic/InputField"; import InputField from "~/components/basic/InputField";
import Button from "../components/basic/buttons/Button"; import Button from "~/components/basic/buttons/Button";
import completeAccountInformationSignupInvite from "./api/auth/CompleteAccountInformationSignupInvite"; import completeAccountInformationSignupInvite from "./api/auth/CompleteAccountInformationSignupInvite";
import Aes256Gcm from "../components/aes-256-gcm"; import Aes256Gcm from "~/components/aes-256-gcm";
import passwordCheck from "../components/utilities/checks/PasswordCheck"; import passwordCheck from "~/utilities/checks/PasswordCheck";
import attemptLogin from "../components/utilities/attemptLogin"; import attemptLogin from "~/utilities/attemptLogin";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCheck, faX, faWarning } from "@fortawesome/free-solid-svg-icons"; import { faCheck, faX, faWarning } from "@fortawesome/free-solid-svg-icons";
import issueBackupKey from "../components/utilities/issueBackupKey"; import issueBackupKey from "~/utilities/issueBackupKey";
import verifySignupInvite from "./api/auth/VerifySignupInvite"; import verifySignupInvite from "./api/auth/VerifySignupInvite";
const nacl = require("tweetnacl"); const nacl = require("tweetnacl");
@@ -76,7 +76,14 @@ export default function SignupInvite() {
const { ciphertext, iv, tag } = Aes256Gcm.encrypt( const { ciphertext, iv, tag } = Aes256Gcm.encrypt(
PRIVATE_KEY, 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); localStorage.setItem("PRIVATE_KEY", PRIVATE_KEY);
@@ -87,18 +94,19 @@ export default function SignupInvite() {
}, },
async () => { async () => {
client.createVerifier(async (err, result) => { client.createVerifier(async (err, result) => {
const response = await completeAccountInformationSignupInvite({ const response =
email, await completeAccountInformationSignupInvite({
firstName, email,
lastName, firstName,
publicKey: PUBLIC_KEY, lastName,
ciphertext, publicKey: PUBLIC_KEY,
iv, ciphertext,
tag, iv,
salt: result.salt, tag,
verifier: result.verifier, salt: result.salt,
token: verificationToken verifier: result.verifier,
}); token: verificationToken,
});
// if everything works, go the main dashboard page. // if everything works, go the main dashboard page.
if (!errorCheck && response.status == "200") { if (!errorCheck && response.status == "200") {
@@ -148,19 +156,19 @@ export default function SignupInvite() {
alt="verify email" alt="verify email"
></Image> ></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"> <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" text="Confirm Email"
onButtonPressed={async () => { onButtonPressed={async () => {
const response = await verifySignupInvite({ const response = await verifySignupInvite({
email, email,
code: token code: token,
}); });
if (response.status == 200) { if (response.status == 200) {
setVerificationToken((await response.json()).token) setVerificationToken((await response.json()).token);
setStep(2); setStep(2);
} else { } else {
console.log("ERROR", response) console.log("ERROR", response);
router.push("/requestnewinvite") router.push("/requestnewinvite");
} }
}} }}
size="lg" size="lg"
@@ -213,29 +221,89 @@ export default function SignupInvite() {
type="password" type="password"
value={password} value={password}
isRequired 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"> {passwordErrorLength ||
<div className={`text-gray-400 text-sm mb-1`}>Password should contain at least:</div> passwordErrorLowerCase ||
<div className="flex flex-row justify-start items-center ml-1"> passwordErrorNumber ? (
{passwordErrorLength <div className="w-full mt-4 bg-white/5 px-2 flex flex-col items-start py-2 rounded-md">
? <FontAwesomeIcon icon={faX} className="text-md text-red mr-2.5"/> <div className={`text-gray-400 text-sm mb-1`}>
: <FontAwesomeIcon icon={faCheck} className="text-md text-primary mr-2"/>} Password should contain at least:
<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">
{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>
<div className="flex flex-row justify-start items-center ml-1"> ) : (
{passwordErrorLowerCase <div className="py-2"></div>
? <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> </div>
<div className="flex flex-col items-center justify-center w-full md:px-4 md:py-5 mt-2 px-2 py-3 max-h-24 max-w-md mx-auto text-lg"> <div className="flex flex-col items-center justify-center 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 <Button
@@ -257,23 +325,32 @@ export default function SignupInvite() {
Save your Emergency Kit Save your Emergency Kit
</p> </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 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>
<div className="mt-3">We recommend you download it and keep it somewhere safe.</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>
<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"> <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"/> <FontAwesomeIcon
It contains your Secret Key which we cannot access or recover for you if you lose it. 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>
<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"> <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 <Button
text="Download PDF" text="Download PDF"
onButtonPressed={async () => { onButtonPressed={async () => {
await issueBackupKey({ await issueBackupKey({
email, email,
password, password,
personalName: (firstName + " " + lastName), personalName: firstName + " " + lastName,
setBackupKeyError, setBackupKeyError,
setBackupKeyIssued setBackupKeyIssued,
}); });
router.push("/dashboard/"); router.push("/dashboard/");
}} }}

View File

@@ -5,17 +5,17 @@ import Image from "next/image";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faMagnifyingGlass, faPlus } from "@fortawesome/free-solid-svg-icons"; import { faMagnifyingGlass, faPlus } from "@fortawesome/free-solid-svg-icons";
import UserTable from "../../components/basic/table/UserTable"; import UserTable from "~/components/basic/table/UserTable";
import getWorkspaceUsers from "../api/workspace/getWorkspaceUsers"; import getWorkspaceUsers from "../api/workspace/getWorkspaceUsers";
import guidGenerator from "../../components/utilities/randomId"; import guidGenerator from "~/utilities/randomId";
import AddProjectMemberDialog from "../../components/basic/dialog/AddProjectMemberDialog"; import AddProjectMemberDialog from "~/components/basic/dialog/AddProjectMemberDialog";
// import DeleteUserDialog from '../../components/basic/dialog/DeleteUserDialog'; // import DeleteUserDialog from '~/components/basic/dialog/DeleteUserDialog';
import addUserToWorkspace from "../api/workspace/addUserToWorkspace"; import addUserToWorkspace from "../api/workspace/addUserToWorkspace";
import getUser from "../api/user/getUser"; import getUser from "../api/user/getUser";
import uploadKeys from "../api/workspace/uploadKeys"; import uploadKeys from "../api/workspace/uploadKeys";
import getOrganizationUsers from "../api/organization/GetOrgUsers"; import getOrganizationUsers from "../api/organization/GetOrgUsers";
import NavHeader from "../../components/navigation/NavHeader"; import NavHeader from "~/components/navigation/NavHeader";
import Button from "../../components/basic/buttons/Button"; import Button from "~/components/basic/buttons/Button";
// #TODO: Update all the workspaceIds // #TODO: Update all the workspaceIds
const crypto = require("crypto"); const crypto = require("crypto");
@@ -98,15 +98,13 @@ export default function Users() {
firstName: user.user?.firstName, firstName: user.user?.firstName,
lastName: user.user?.lastName, lastName: user.user?.lastName,
email: email:
user.user?.email == null user.user?.email == null ? user.inviteEmail : user.user?.email,
? user.inviteEmail
: user.user?.email,
role: user?.role, role: user?.role,
status: user?.status, status: user?.status,
userId: user.user?._id, userId: user.user?._id,
membershipId: user._id, membershipId: user._id,
publicKey: user.user?.publicKey, publicKey: user.user?.publicKey,
})) }));
setUserList(tempUserList); setUserList(tempUserList);
const orgUsers = await getOrganizationUsers({ const orgUsers = await getOrganizationUsers({
orgId: localStorage.getItem("orgData.id"), orgId: localStorage.getItem("orgData.id"),
@@ -114,15 +112,15 @@ export default function Users() {
setOrgUserList(orgUsers); setOrgUserList(orgUsers);
setEmail( setEmail(
orgUsers orgUsers
?.filter((user) => user.status == "accepted") ?.filter((user) => user.status == "accepted")
.map((user) => user.user.email) .map((user) => user.user.email)
.filter( .filter(
(email) => (email) =>
!tempUserList !tempUserList
?.map((user1) => user1.email) ?.map((user1) => user1.email)
.includes(email) .includes(email)
)[0] )[0]
) );
}, []); }, []);
return userList ? ( return userList ? (
@@ -131,7 +129,7 @@ export default function Users() {
<title>Users</title> <title>Users</title>
<link rel="icon" href="/infisical.ico" /> <link rel="icon" href="/infisical.ico" />
</Head> </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"> <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="font-semibold mr-4 text-white">Project Members</p>
<p className="mr-4 text-base text-gray-400"> <p className="mr-4 text-base text-gray-400">