mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 22:28:15 +00:00
Continue removing unused frontend components/logic, improve querying in select pages
This commit is contained in:
@@ -1,99 +0,0 @@
|
|||||||
import { Fragment, useState } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
|
||||||
|
|
||||||
import addIncidentContact from "@app/pages/api/organization/addIncidentContact";
|
|
||||||
|
|
||||||
import Button from "../buttons/Button";
|
|
||||||
import InputField from "../InputField";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
isOpen: boolean;
|
|
||||||
closeModal: () => void;
|
|
||||||
incidentContacts: string[];
|
|
||||||
setIncidentContacts: (arg: string[]) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const AddIncidentContactDialog = ({
|
|
||||||
isOpen,
|
|
||||||
closeModal,
|
|
||||||
incidentContacts,
|
|
||||||
setIncidentContacts
|
|
||||||
}: Props) => {
|
|
||||||
const [incidentContactEmail, setIncidentContactEmail] = useState("");
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const submit = () => {
|
|
||||||
setIncidentContacts(
|
|
||||||
incidentContacts?.length > 0
|
|
||||||
? incidentContacts.concat([incidentContactEmail])
|
|
||||||
: [incidentContactEmail]
|
|
||||||
);
|
|
||||||
addIncidentContact(localStorage.getItem("orgData.id") as string, incidentContactEmail);
|
|
||||||
closeModal();
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Transition appear show={isOpen} as={Fragment}>
|
|
||||||
<Dialog as="div" className="relative z-10" onClose={closeModal}>
|
|
||||||
<Transition.Child
|
|
||||||
as={Fragment}
|
|
||||||
enter="ease-out duration-300"
|
|
||||||
enterFrom="opacity-0"
|
|
||||||
enterTo="opacity-100"
|
|
||||||
leave="ease-in duration-200"
|
|
||||||
leaveFrom="opacity-100"
|
|
||||||
leaveTo="opacity-0"
|
|
||||||
>
|
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-70" />
|
|
||||||
</Transition.Child>
|
|
||||||
|
|
||||||
<div className="fixed inset-0 overflow-y-auto">
|
|
||||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
|
||||||
<Transition.Child
|
|
||||||
as={Fragment}
|
|
||||||
enter="ease-out duration-300"
|
|
||||||
enterFrom="opacity-0 scale-95"
|
|
||||||
enterTo="opacity-100 scale-100"
|
|
||||||
leave="ease-in duration-200"
|
|
||||||
leaveFrom="opacity-100 scale-100"
|
|
||||||
leaveTo="opacity-0 scale-95"
|
|
||||||
>
|
|
||||||
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md border border-gray-700 bg-bunker-800 p-6 text-left align-middle shadow-xl transition-all">
|
|
||||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-400">
|
|
||||||
{t("section.incident.add-dialog.title")}
|
|
||||||
</Dialog.Title>
|
|
||||||
<div className="mt-2 mb-2">
|
|
||||||
<p className="text-sm text-gray-500">
|
|
||||||
{t("section.incident.add-dialog.description")}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="max-h-28">
|
|
||||||
<InputField
|
|
||||||
label={t("common.email")}
|
|
||||||
onChangeHandler={setIncidentContactEmail}
|
|
||||||
type="varName"
|
|
||||||
value={incidentContactEmail}
|
|
||||||
placeholder=""
|
|
||||||
isRequired
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mt-6 max-w-max">
|
|
||||||
<Button
|
|
||||||
onButtonPressed={submit}
|
|
||||||
color="mineshaft"
|
|
||||||
text={t("section.incident.add-dialog.add-incident") as string}
|
|
||||||
size="md"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Dialog.Panel>
|
|
||||||
</Transition.Child>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
|
||||||
</Transition>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AddIncidentContactDialog;
|
|
||||||
@@ -6,8 +6,10 @@ import { useNotificationContext } from "@app/components/context/Notifications/No
|
|||||||
import { Select, SelectItem } from "@app/components/v2";
|
import { Select, SelectItem } from "@app/components/v2";
|
||||||
import { useSubscription, useWorkspace } from "@app/context";
|
import { useSubscription, useWorkspace } from "@app/context";
|
||||||
import updateUserProjectPermission from "@app/ee/api/memberships/UpdateUserProjectPermission";
|
import updateUserProjectPermission from "@app/ee/api/memberships/UpdateUserProjectPermission";
|
||||||
import changeUserRoleInWorkspace from "@app/pages/api/workspace/changeUserRoleInWorkspace";
|
import {
|
||||||
import deleteUserFromWorkspace from "@app/pages/api/workspace/deleteUserFromWorkspace";
|
useDeleteUserFromWorkspace,
|
||||||
|
useUpdateUserWorkspaceRole
|
||||||
|
} from "@app/hooks/api";
|
||||||
import getLatestFileKey from "@app/pages/api/workspace/getLatestFileKey";
|
import getLatestFileKey from "@app/pages/api/workspace/getLatestFileKey";
|
||||||
import uploadKeys from "@app/pages/api/workspace/uploadKeys";
|
import uploadKeys from "@app/pages/api/workspace/uploadKeys";
|
||||||
|
|
||||||
@@ -40,9 +42,11 @@ type EnvironmentProps = {
|
|||||||
const ProjectUsersTable = ({ userData, changeData, myUser, filter, isUserListLoading }: Props) => {
|
const ProjectUsersTable = ({ userData, changeData, myUser, filter, isUserListLoading }: Props) => {
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
const { subscription } = useSubscription();
|
const { subscription } = useSubscription();
|
||||||
const [roleSelected, setRoleSelected] = useState(
|
const { mutateAsync: deleteUserFromWorkspaceMutateAsync } = useDeleteUserFromWorkspace();
|
||||||
Array(userData?.length).fill(userData.map((user) => user.role))
|
const { mutateAsync: updateUserWorkspaceRoleMutateAsync } = useUpdateUserWorkspaceRole();
|
||||||
);
|
// const [roleSelected, setRoleSelected] = useState(
|
||||||
|
// Array(userData?.length).fill(userData.map((user) => user.role))
|
||||||
|
// );
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [myRole, setMyRole] = useState("member");
|
const [myRole, setMyRole] = useState("member");
|
||||||
const [workspaceEnvs, setWorkspaceEnvs] = useState<EnvironmentProps[]>([]);
|
const [workspaceEnvs, setWorkspaceEnvs] = useState<EnvironmentProps[]>([]);
|
||||||
@@ -52,38 +56,15 @@ const ProjectUsersTable = ({ userData, changeData, myUser, filter, isUserListLoa
|
|||||||
const workspaceId = router.query.id as string;
|
const workspaceId = router.query.id as string;
|
||||||
// Delete the row in the table (e.g. a user)
|
// Delete the row in the table (e.g. a user)
|
||||||
// #TODO: Add a pop-up that warns you that the user is going to be deleted.
|
// #TODO: Add a pop-up that warns you that the user is going to be deleted.
|
||||||
const handleDelete = (membershipId: string, index: number) => {
|
const handleDelete = async (membershipId: string) => {
|
||||||
// setUserIdToBeDeleted(userId);
|
await deleteUserFromWorkspaceMutateAsync(membershipId);
|
||||||
// onClick();
|
|
||||||
deleteUserFromWorkspace(membershipId);
|
|
||||||
changeData(userData.filter((v, i) => i !== index));
|
|
||||||
setRoleSelected([
|
|
||||||
...roleSelected.slice(0, index),
|
|
||||||
...roleSelected.slice(index + 1, userData?.length)
|
|
||||||
]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update the rold of a certain user
|
const handleRoleUpdate = async (index: number, e: string) => {
|
||||||
const handleRoleUpdate = (index: number, e: string) => {
|
await updateUserWorkspaceRoleMutateAsync({
|
||||||
changeUserRoleInWorkspace(userData[index].membershipId, e.toLowerCase());
|
membershipId: userData[index].membershipId,
|
||||||
changeData([
|
role: e.toLowerCase()
|
||||||
...userData.slice(0, index),
|
});
|
||||||
...[
|
|
||||||
{
|
|
||||||
key: userData[index].key,
|
|
||||||
firstName: userData[index].firstName,
|
|
||||||
lastName: userData[index].lastName,
|
|
||||||
email: userData[index].email,
|
|
||||||
role: e.toLocaleLowerCase(),
|
|
||||||
status: userData[index].status,
|
|
||||||
userId: userData[index].userId,
|
|
||||||
membershipId: userData[index].membershipId,
|
|
||||||
publicKey: userData[index].publicKey,
|
|
||||||
deniedPermissions: userData[index].deniedPermissions
|
|
||||||
}
|
|
||||||
],
|
|
||||||
...userData.slice(index + 1, userData?.length)
|
|
||||||
]);
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text: "Successfully changed user role.",
|
text: "Successfully changed user role.",
|
||||||
type: "success"
|
type: "success"
|
||||||
@@ -373,7 +354,7 @@ const ProjectUsersTable = ({ userData, changeData, myUser, filter, isUserListLoa
|
|||||||
myRole !== "member" ? (
|
myRole !== "member" ? (
|
||||||
<div className="mt-0.5 flex items-center opacity-50 hover:opacity-100">
|
<div className="mt-0.5 flex items-center opacity-50 hover:opacity-100">
|
||||||
<Button
|
<Button
|
||||||
onButtonPressed={() => handleDelete(row.membershipId, index)}
|
onButtonPressed={() => handleDelete(row.membershipId)}
|
||||||
color="red"
|
color="red"
|
||||||
size="icon-sm"
|
size="icon-sm"
|
||||||
icon={faX}
|
icon={faX}
|
||||||
|
|||||||
@@ -1,122 +0,0 @@
|
|||||||
import Image from "next/image";
|
|
||||||
import { faCheck, faXmark } from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
|
|
||||||
import deleteIntegrationAuth from "../../pages/api/integrations/DeleteIntegrationAuth";
|
|
||||||
|
|
||||||
interface IntegrationOption {
|
|
||||||
clientId: string;
|
|
||||||
clientSlug?: string; // vercel-integration specific
|
|
||||||
docsLink: string;
|
|
||||||
image: string;
|
|
||||||
isAvailable: boolean;
|
|
||||||
name: string;
|
|
||||||
slug: string;
|
|
||||||
type: string;
|
|
||||||
}
|
|
||||||
interface IntegrationAuth {
|
|
||||||
_id: string;
|
|
||||||
integration: string;
|
|
||||||
workspace: string;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
cloudIntegrationOption: IntegrationOption;
|
|
||||||
setSelectedIntegrationOption: (cloudIntegration: IntegrationOption) => void;
|
|
||||||
integrationOptionPress: (cloudIntegrationOption: IntegrationOption) => void;
|
|
||||||
integrationAuths: IntegrationAuth[];
|
|
||||||
handleDeleteIntegrationAuth: (args: { integrationAuth: IntegrationAuth }) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CloudIntegration = ({
|
|
||||||
cloudIntegrationOption,
|
|
||||||
setSelectedIntegrationOption,
|
|
||||||
integrationOptionPress,
|
|
||||||
integrationAuths,
|
|
||||||
handleDeleteIntegrationAuth
|
|
||||||
}: Props) => {
|
|
||||||
return integrationAuths ? (
|
|
||||||
<div
|
|
||||||
onKeyDown={() => null}
|
|
||||||
role="button"
|
|
||||||
tabIndex={0}
|
|
||||||
className={`relative ${
|
|
||||||
cloudIntegrationOption.isAvailable
|
|
||||||
? "cursor-pointer duration-200 hover:bg-mineshaft-700"
|
|
||||||
: "opacity-50"
|
|
||||||
} flex h-32 flex-row items-center rounded-md bg-mineshaft-800 border border-mineshaft-600 p-4`}
|
|
||||||
onClick={() => {
|
|
||||||
if (!cloudIntegrationOption.isAvailable) return;
|
|
||||||
setSelectedIntegrationOption(cloudIntegrationOption);
|
|
||||||
integrationOptionPress(cloudIntegrationOption);
|
|
||||||
}}
|
|
||||||
key={cloudIntegrationOption.name}
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
src={`/images/integrations/${cloudIntegrationOption.image}`}
|
|
||||||
height={70}
|
|
||||||
width={70}
|
|
||||||
alt="integration logo"
|
|
||||||
/>
|
|
||||||
{cloudIntegrationOption.name.split(" ").length > 2 ? (
|
|
||||||
<div className="ml-4 max-w-xs text-3xl font-semibold text-gray-300 duration-200 group-hover:text-gray-200">
|
|
||||||
<div>{cloudIntegrationOption.name.split(" ")[0]}</div>
|
|
||||||
<div className="text-base">
|
|
||||||
{cloudIntegrationOption.name.split(" ")[1]} {cloudIntegrationOption.name.split(" ")[2]}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="ml-4 max-w-xs text-xl font-semibold text-gray-300 duration-200 group-hover:text-gray-200">
|
|
||||||
{cloudIntegrationOption.name}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{cloudIntegrationOption.isAvailable &&
|
|
||||||
integrationAuths
|
|
||||||
.map((authorization) => authorization?.integration)
|
|
||||||
.includes(cloudIntegrationOption.slug) && (
|
|
||||||
<div className="group absolute top-0 right-0 z-40 flex flex-row">
|
|
||||||
<div
|
|
||||||
onKeyDown={() => null}
|
|
||||||
role="button"
|
|
||||||
tabIndex={0}
|
|
||||||
onClick={async (event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
const deletedIntegrationAuth = await deleteIntegrationAuth({
|
|
||||||
integrationAuthId: integrationAuths
|
|
||||||
.filter(
|
|
||||||
(authorization) => authorization.integration === cloudIntegrationOption.slug
|
|
||||||
)
|
|
||||||
.map((authorization) => authorization._id)[0]
|
|
||||||
});
|
|
||||||
|
|
||||||
handleDeleteIntegrationAuth({
|
|
||||||
integrationAuth: deletedIntegrationAuth
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
className="flex w-max cursor-pointer flex-row items-center rounded-bl-md bg-red py-0.5 px-2 text-xs opacity-30 duration-200 group-hover:opacity-100"
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faXmark} className="mr-2 text-xs" />
|
|
||||||
Revoke
|
|
||||||
</div>
|
|
||||||
<div className="flex w-max flex-row items-center rounded-tr-md bg-primary py-0.5 px-2 text-xs text-black opacity-70 duration-200 group-hover:opacity-100">
|
|
||||||
<FontAwesomeIcon icon={faCheck} className="mr-2 text-xs" />
|
|
||||||
Authorized
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{!cloudIntegrationOption.isAvailable && (
|
|
||||||
<div className="group absolute top-0 right-0 z-50 flex flex-row">
|
|
||||||
<div className="flex w-max flex-row items-center rounded-bl-md rounded-tr-md bg-yellow py-0.5 px-2 text-xs text-black opacity-90">
|
|
||||||
Coming Soon
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div />
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CloudIntegration;
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
import CloudIntegration from "./CloudIntegration";
|
|
||||||
|
|
||||||
interface IntegrationOption {
|
|
||||||
clientId: string;
|
|
||||||
clientSlug?: string; // vercel-integration specific
|
|
||||||
docsLink: string;
|
|
||||||
image: string;
|
|
||||||
isAvailable: boolean;
|
|
||||||
name: string;
|
|
||||||
slug: string;
|
|
||||||
type: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IntegrationAuth {
|
|
||||||
_id: string;
|
|
||||||
integration: string;
|
|
||||||
workspace: string;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
cloudIntegrationOptions: IntegrationOption[];
|
|
||||||
setSelectedIntegrationOption: () => void;
|
|
||||||
integrationOptionPress: (integrationOption: IntegrationOption) => void;
|
|
||||||
integrationAuths: IntegrationAuth[];
|
|
||||||
handleDeleteIntegrationAuth: (args: { integrationAuth: IntegrationAuth }) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CloudIntegrationSection = ({
|
|
||||||
cloudIntegrationOptions,
|
|
||||||
setSelectedIntegrationOption,
|
|
||||||
integrationOptionPress,
|
|
||||||
integrationAuths,
|
|
||||||
handleDeleteIntegrationAuth
|
|
||||||
}: Props) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="m-4 mt-7 flex max-w-5xl flex-col items-start justify-between px-2 text-xl">
|
|
||||||
<h1 className="text-3xl font-semibold">{t("integrations.cloud-integrations")}</h1>
|
|
||||||
<p className="text-base text-gray-400">{t("integrations.click-to-start")}</p>
|
|
||||||
</div>
|
|
||||||
<div className="mx-6 grid max-w-5xl grid-cols-4 grid-rows-2 gap-4">
|
|
||||||
{cloudIntegrationOptions.map((cloudIntegrationOption) => (
|
|
||||||
<CloudIntegration
|
|
||||||
cloudIntegrationOption={cloudIntegrationOption}
|
|
||||||
setSelectedIntegrationOption={setSelectedIntegrationOption}
|
|
||||||
integrationOptionPress={integrationOptionPress}
|
|
||||||
integrationAuths={integrationAuths}
|
|
||||||
handleDeleteIntegrationAuth={handleDeleteIntegrationAuth}
|
|
||||||
key={`cloud-integration-${cloudIntegrationOption.slug}`}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CloudIntegrationSection;
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import Image from "next/image";
|
|
||||||
|
|
||||||
interface Framework {
|
|
||||||
name: string;
|
|
||||||
slug: string;
|
|
||||||
image: string;
|
|
||||||
docsLink: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const FrameworkIntegration = ({ framework }: { framework: Framework }) => (
|
|
||||||
<a
|
|
||||||
href={framework.docsLink}
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
target="_blank"
|
|
||||||
className="relative flex flex-row justify-center duration-200 h-32 rounded-md p-0.5 items-center cursor-pointer"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={`hover:bg-mineshaft-700 cursor-pointer font-semibold bg-mineshaft-800 border border-mineshaft-600 flex flex-col items-center justify-center h-full w-full rounded-md text-gray-300 group-hover:text-gray-200 duration-200 ${
|
|
||||||
framework?.name?.split(" ").length > 1 ? "text-sm px-1" : "text-xl px-2"
|
|
||||||
} text-center w-full max-w-xs`}
|
|
||||||
>
|
|
||||||
{framework?.image && (
|
|
||||||
<Image
|
|
||||||
src={`/images/integrations/${framework.image}.png`}
|
|
||||||
height={framework?.name ? 60 : 90}
|
|
||||||
width={framework?.name ? 60 : 90}
|
|
||||||
alt="integration logo"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{framework?.name && framework?.image && <div className="h-2" />}
|
|
||||||
{framework?.name && framework.name}
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default FrameworkIntegration;
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
import FrameworkIntegration from "./FrameworkIntegration";
|
|
||||||
|
|
||||||
interface Framework {
|
|
||||||
name: string;
|
|
||||||
image: string;
|
|
||||||
link: string;
|
|
||||||
slug: string;
|
|
||||||
docsLink: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
frameworks: [Framework];
|
|
||||||
}
|
|
||||||
|
|
||||||
const FrameworkIntegrationSection = ({ frameworks }: Props) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="mx-4 mt-12 mb-4 flex max-w-5xl flex-col items-start justify-between px-2 text-xl">
|
|
||||||
<h1 className="text-3xl font-semibold">{t("integrations.framework-integrations")}</h1>
|
|
||||||
<p className="text-base text-gray-400">{t("integrations.click-to-setup")}</p>
|
|
||||||
</div>
|
|
||||||
<div className="mx-6 mt-4 grid max-w-5xl grid-cols-7 grid-rows-2 gap-4">
|
|
||||||
{frameworks.map((framework) => (
|
|
||||||
<FrameworkIntegration
|
|
||||||
framework={framework}
|
|
||||||
key={`framework-integration-${framework.slug}`}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FrameworkIntegrationSection;
|
|
||||||
@@ -1,313 +0,0 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import { faArrowRight, faCheck, faXmark } from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
// TODO: This needs to be moved from public folder
|
|
||||||
import {
|
|
||||||
contextNetlifyMapping,
|
|
||||||
integrationSlugNameMapping,
|
|
||||||
reverseContextNetlifyMapping
|
|
||||||
} from "public/data/frequentConstants";
|
|
||||||
|
|
||||||
import Button from "@app/components/basic/buttons/Button";
|
|
||||||
import ListBox from "@app/components/basic/Listbox";
|
|
||||||
|
|
||||||
import deleteIntegration from "../../pages/api/integrations/DeleteIntegration";
|
|
||||||
import getIntegrationApps from "../../pages/api/integrations/GetIntegrationApps";
|
|
||||||
import updateIntegration from "../../pages/api/integrations/updateIntegration";
|
|
||||||
|
|
||||||
interface Integration {
|
|
||||||
_id: string;
|
|
||||||
isActive: boolean;
|
|
||||||
app: string | null;
|
|
||||||
appId: string | null;
|
|
||||||
path: string | null;
|
|
||||||
region: string | null;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
environment: string;
|
|
||||||
integration: string;
|
|
||||||
targetEnvironment: string;
|
|
||||||
workspace: string;
|
|
||||||
integrationAuth: string;
|
|
||||||
secretPath: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IntegrationApp {
|
|
||||||
name: string;
|
|
||||||
appId?: string;
|
|
||||||
owner?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
integration: Integration;
|
|
||||||
integrations: Integration[];
|
|
||||||
setIntegrations: any;
|
|
||||||
bot: any;
|
|
||||||
setBot: any;
|
|
||||||
environments: Array<{ name: string; slug: string }>;
|
|
||||||
handleDeleteIntegration: (args: { integration: Integration }) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: refactor
|
|
||||||
const IntegrationTile = ({
|
|
||||||
integration,
|
|
||||||
integrations,
|
|
||||||
bot,
|
|
||||||
setBot,
|
|
||||||
setIntegrations,
|
|
||||||
environments = [],
|
|
||||||
handleDeleteIntegration
|
|
||||||
}: Props) => {
|
|
||||||
const [integrationEnvironment, setIntegrationEnvironment] = useState<Props["environments"][0]>(
|
|
||||||
environments.find(({ slug }) => slug === integration?.environment) || {
|
|
||||||
name: "",
|
|
||||||
slug: ""
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const router = useRouter();
|
|
||||||
const [apps, setApps] = useState<IntegrationApp[]>([]); // integration app objects
|
|
||||||
const [integrationApp, setIntegrationApp] = useState(""); // integration app name
|
|
||||||
const [integrationTargetEnvironment, setIntegrationTargetEnvironment] = useState("");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const loadIntegration = async () => {
|
|
||||||
const tempApps: [IntegrationApp] = await getIntegrationApps({
|
|
||||||
integrationAuthId: integration?.integrationAuth
|
|
||||||
});
|
|
||||||
|
|
||||||
setApps(tempApps);
|
|
||||||
|
|
||||||
if (integration?.app) {
|
|
||||||
setIntegrationApp(integration.app);
|
|
||||||
} else if (integration?.path && integration?.region) {
|
|
||||||
setIntegrationApp(`${integration.path} (${integration.region})`);
|
|
||||||
} else if (tempApps.length > 0) {
|
|
||||||
setIntegrationApp(tempApps[0].name);
|
|
||||||
} else {
|
|
||||||
setIntegrationApp("");
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (integration.integration) {
|
|
||||||
case "vercel":
|
|
||||||
setIntegrationTargetEnvironment(
|
|
||||||
integration?.targetEnvironment
|
|
||||||
? integration.targetEnvironment.charAt(0).toUpperCase() +
|
|
||||||
integration.targetEnvironment.substring(1)
|
|
||||||
: "Development"
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case "netlify":
|
|
||||||
setIntegrationTargetEnvironment(
|
|
||||||
integration?.targetEnvironment
|
|
||||||
? contextNetlifyMapping[integration.targetEnvironment]
|
|
||||||
: "Local development"
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
loadIntegration();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleStartIntegration = async () => {
|
|
||||||
const reformatTargetEnvironment = (targetEnvironment: string) => {
|
|
||||||
switch (integration.integration) {
|
|
||||||
case "vercel":
|
|
||||||
return targetEnvironment.toLowerCase();
|
|
||||||
case "netlify":
|
|
||||||
return reverseContextNetlifyMapping[targetEnvironment];
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const siteApp = apps.find((app) => app.name === integrationApp); // obj or undefined
|
|
||||||
const appId = siteApp?.appId ?? null;
|
|
||||||
const owner = siteApp?.owner ?? null;
|
|
||||||
|
|
||||||
// return updated integration
|
|
||||||
const updatedIntegration = await updateIntegration({
|
|
||||||
integrationId: integration._id,
|
|
||||||
environment: integrationEnvironment.slug,
|
|
||||||
isActive: true,
|
|
||||||
app: integrationApp,
|
|
||||||
appId,
|
|
||||||
targetEnvironment: reformatTargetEnvironment(integrationTargetEnvironment),
|
|
||||||
owner
|
|
||||||
});
|
|
||||||
|
|
||||||
setIntegrations(
|
|
||||||
integrations.map((i) => (i._id === updatedIntegration._id ? updatedIntegration : i))
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
||||||
const renderIntegrationSpecificParams = (integration: Integration) => {
|
|
||||||
try {
|
|
||||||
switch (integration.integration) {
|
|
||||||
case "vercel":
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="mb-2 w-60 text-xs font-semibold text-gray-400">ENVIRONMENT</div>
|
|
||||||
<ListBox
|
|
||||||
data={!integration.isActive ? ["Development", "Preview", "Production"] : null}
|
|
||||||
isSelected={integrationTargetEnvironment}
|
|
||||||
onChange={setIntegrationTargetEnvironment}
|
|
||||||
isFull
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case "netlify":
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="mb-2 text-xs font-semibold text-gray-400">CONTEXT</div>
|
|
||||||
<ListBox
|
|
||||||
data={
|
|
||||||
!integration.isActive
|
|
||||||
? ["Production", "Deploy previews", "Branch deploys", "Local development"]
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
isSelected={integrationTargetEnvironment}
|
|
||||||
onChange={setIntegrationTargetEnvironment}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case "railway":
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="mb-2 text-xs font-semibold text-gray-400">ENVIRONMENT</div>
|
|
||||||
<ListBox
|
|
||||||
data={
|
|
||||||
!integration.isActive
|
|
||||||
? ["Production", "Deploy previews", "Branch deploys", "Local development"]
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
isSelected={integration.targetEnvironment}
|
|
||||||
onChange={setIntegrationTargetEnvironment}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case "gitlab":
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="mb-2 text-xs font-semibold text-gray-400">ENVIRONMENT</div>
|
|
||||||
<ListBox
|
|
||||||
data={null}
|
|
||||||
isSelected={integration.targetEnvironment}
|
|
||||||
onChange={setIntegrationTargetEnvironment}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return <div />;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div />;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!integrationApp && integration.integration !== "checkly") return <div />;
|
|
||||||
|
|
||||||
const isSelected =
|
|
||||||
integration.integration === "hashicorp-vault"
|
|
||||||
? `${integration.app} - path: ${integration.path}`
|
|
||||||
: integrationApp;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="mx-6 mb-8 flex max-w-5xl justify-between rounded-md border border-mineshaft-600 bg-mineshaft-800 p-6">
|
|
||||||
<div className="flex">
|
|
||||||
<div>
|
|
||||||
<p className="mb-2 text-xs font-semibold text-gray-400">ENVIRONMENT</p>
|
|
||||||
<ListBox
|
|
||||||
data={!integration.isActive ? environments.map(({ name }) => name) : null}
|
|
||||||
isSelected={integrationEnvironment.name}
|
|
||||||
onChange={(envName) =>
|
|
||||||
setIntegrationEnvironment(
|
|
||||||
environments.find(({ name }) => envName === name) || {
|
|
||||||
name: "unknown",
|
|
||||||
slug: "unknown"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
isFull
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="ml-2">
|
|
||||||
<p className="mb-2 text-xs font-semibold text-gray-400">SECRET PATH</p>
|
|
||||||
<div className="cursor-default rounded-md bg-white/[.07] py-2.5 pl-4 pr-10 text-sm font-semibold text-gray-300">
|
|
||||||
{/* {integration.integration.charAt(0).toUpperCase() + integration.integration.slice(1)} */}
|
|
||||||
{integration.secretPath}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="pt-2">
|
|
||||||
<FontAwesomeIcon icon={faArrowRight} className="mx-4 mt-8 text-gray-400" />
|
|
||||||
</div>
|
|
||||||
<div className="mr-2">
|
|
||||||
<p className="mb-2 text-xs font-semibold text-gray-400">INTEGRATION</p>
|
|
||||||
<div className="cursor-default rounded-md bg-white/[.07] py-2.5 pl-4 pr-10 text-sm font-semibold text-gray-300">
|
|
||||||
{/* {integration.integration.charAt(0).toUpperCase() + integration.integration.slice(1)} */}
|
|
||||||
{integrationSlugNameMapping[integration.integration]}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mr-2">
|
|
||||||
<div className="mb-2 text-xs font-semibold text-gray-400">APP</div>
|
|
||||||
{integrationApp ? (
|
|
||||||
<div title={integrationApp}>
|
|
||||||
<ListBox
|
|
||||||
data={!integration.isActive ? apps.map((app) => app.name) : null}
|
|
||||||
isSelected={isSelected}
|
|
||||||
onChange={(app) => {
|
|
||||||
setIntegrationApp(app);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="h-10 w-52 animate-pulse rounded-md bg-mineshaft-600 px-4 py-2 font-bold">
|
|
||||||
-
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{renderIntegrationSpecificParams(integration)}
|
|
||||||
</div>
|
|
||||||
<div className="flex cursor-default items-end">
|
|
||||||
{integration.isActive ? (
|
|
||||||
<div className="flex max-w-5xl flex-row items-center rounded-md border border-mineshaft-500 bg-mineshaft-600 p-[0.44rem] px-4">
|
|
||||||
<FontAwesomeIcon icon={faCheck} className="mr-2.5 text-lg text-primary" />
|
|
||||||
<div className="font-semibold text-gray-300">In Sync</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Button
|
|
||||||
text="Start Integration"
|
|
||||||
onButtonPressed={() => handleStartIntegration()}
|
|
||||||
color="mineshaft"
|
|
||||||
size="md"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div className="ml-2 opacity-80 duration-200 hover:opacity-100">
|
|
||||||
<Button
|
|
||||||
onButtonPressed={() =>
|
|
||||||
handleDeleteIntegration({
|
|
||||||
integration
|
|
||||||
})
|
|
||||||
}
|
|
||||||
color="red"
|
|
||||||
size="icon-md"
|
|
||||||
icon={faXmark}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default IntegrationTile;
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
import IntegrationTile from "./Integration";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
integrations: any;
|
|
||||||
setIntegrations: any;
|
|
||||||
bot: any;
|
|
||||||
setBot: any;
|
|
||||||
environments: Array<{ name: string; slug: string }>;
|
|
||||||
handleDeleteIntegration: (args: { integration: Integration }) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Integration {
|
|
||||||
_id: string;
|
|
||||||
isActive: boolean;
|
|
||||||
app: string | null;
|
|
||||||
appId: string | null;
|
|
||||||
path: string | null;
|
|
||||||
region: string | null;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
environment: string;
|
|
||||||
integration: string;
|
|
||||||
targetEnvironment: string;
|
|
||||||
workspace: string;
|
|
||||||
integrationAuth: string;
|
|
||||||
secretPath: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ProjectIntegrationSection = ({
|
|
||||||
integrations,
|
|
||||||
setIntegrations,
|
|
||||||
bot,
|
|
||||||
setBot,
|
|
||||||
environments = [],
|
|
||||||
handleDeleteIntegration
|
|
||||||
}: Props) => {
|
|
||||||
return integrations.length > 0 ? (
|
|
||||||
<div className="mb-12">
|
|
||||||
<div className="mx-4 mb-4 mt-6 flex max-w-5xl flex-col items-start justify-between px-2 text-xl">
|
|
||||||
<h1 className="text-3xl font-semibold">Current Integrations</h1>
|
|
||||||
<p className="text-base text-bunker-300">Manage integrations with third-party services.</p>
|
|
||||||
</div>
|
|
||||||
{integrations.map((integration: Integration) => {
|
|
||||||
return (
|
|
||||||
<IntegrationTile
|
|
||||||
key={`integration-${integration?._id.toString()}`}
|
|
||||||
integration={integration}
|
|
||||||
integrations={integrations}
|
|
||||||
bot={bot}
|
|
||||||
setBot={setBot}
|
|
||||||
setIntegrations={setIntegrations}
|
|
||||||
environments={environments}
|
|
||||||
handleDeleteIntegration={handleDeleteIntegration}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div />
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ProjectIntegrationSection;
|
|
||||||
@@ -91,65 +91,55 @@ const initProjectHelper = async ({
|
|||||||
organizationId: string;
|
organizationId: string;
|
||||||
projectName: string;
|
projectName: string;
|
||||||
}) => {
|
}) => {
|
||||||
let project;
|
// create new project
|
||||||
try {
|
const { data: { workspace } } = await createWorkspace({
|
||||||
|
workspaceName: projectName,
|
||||||
|
organizationId
|
||||||
|
});
|
||||||
|
|
||||||
|
// create and upload new (encrypted) project key
|
||||||
|
const randomBytes = crypto.randomBytes(16).toString("hex");
|
||||||
|
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY");
|
||||||
|
|
||||||
|
if (!PRIVATE_KEY) throw new Error("Failed to find private key");
|
||||||
|
|
||||||
// create new project
|
const user = await fetchUserDetails();
|
||||||
const { data: { workspace } } = await createWorkspace({
|
|
||||||
workspaceName: projectName,
|
|
||||||
organizationId
|
|
||||||
});
|
|
||||||
|
|
||||||
project = workspace;
|
|
||||||
|
|
||||||
// create and upload new (encrypted) project key
|
const { ciphertext, nonce } = encryptAssymmetric({
|
||||||
const randomBytes = crypto.randomBytes(16).toString("hex");
|
plaintext: randomBytes,
|
||||||
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY");
|
publicKey: user.publicKey,
|
||||||
|
privateKey: PRIVATE_KEY
|
||||||
if (!PRIVATE_KEY) throw new Error("Failed to find private key");
|
});
|
||||||
|
|
||||||
const user = await fetchUserDetails();
|
await uploadKeys(workspace._id, user._id, ciphertext, nonce);
|
||||||
|
|
||||||
const { ciphertext, nonce } = encryptAssymmetric({
|
// encrypt and upload secrets to new project
|
||||||
plaintext: randomBytes,
|
const secrets = await encryptSecrets({
|
||||||
publicKey: user.publicKey,
|
secretsToEncrypt: secretsToBeAdded,
|
||||||
privateKey: PRIVATE_KEY
|
workspaceId: workspace._id,
|
||||||
});
|
env: "dev"
|
||||||
|
});
|
||||||
await uploadKeys(project._id, user._id, ciphertext, nonce);
|
|
||||||
|
secrets?.forEach((secret) => {
|
||||||
const workspaceId = project._id;
|
createSecret({
|
||||||
|
workspaceId: workspace._id,
|
||||||
// encrypt and upload secrets to new project
|
environment: secret.environment,
|
||||||
const secrets = await encryptSecrets({
|
type: secret.type,
|
||||||
secretsToEncrypt: secretsToBeAdded,
|
secretKey: secret.secretName,
|
||||||
workspaceId,
|
secretKeyCiphertext: secret.secretKeyCiphertext,
|
||||||
env: "dev"
|
secretKeyIV: secret.secretKeyIV,
|
||||||
});
|
secretKeyTag: secret.secretKeyTag,
|
||||||
|
secretValueCiphertext: secret.secretValueCiphertext,
|
||||||
secrets?.forEach((secret) => {
|
secretValueIV: secret.secretValueIV,
|
||||||
createSecret({
|
secretValueTag: secret.secretValueTag,
|
||||||
workspaceId,
|
secretCommentCiphertext: secret.secretCommentCiphertext,
|
||||||
environment: secret.environment,
|
secretCommentIV: secret.secretCommentIV,
|
||||||
type: secret.type,
|
secretCommentTag: secret.secretCommentTag,
|
||||||
secretKey: secret.secretName,
|
secretPath: "/"
|
||||||
secretKeyCiphertext: secret.secretKeyCiphertext,
|
});
|
||||||
secretKeyIV: secret.secretKeyIV,
|
});
|
||||||
secretKeyTag: secret.secretKeyTag,
|
|
||||||
secretValueCiphertext: secret.secretValueCiphertext,
|
return workspace;
|
||||||
secretValueIV: secret.secretValueIV,
|
|
||||||
secretValueTag: secret.secretValueTag,
|
|
||||||
secretCommentCiphertext: secret.secretCommentCiphertext,
|
|
||||||
secretCommentIV: secret.secretCommentIV,
|
|
||||||
secretCommentTag: secret.secretCommentTag,
|
|
||||||
secretPath: "/"
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Failed to init project in organization", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
return project;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|||||||
@@ -8,18 +8,16 @@ const incidentContactKeys = {
|
|||||||
getAllContact: (orgId: string) => ["org-incident-contacts", { orgId }] as const
|
getAllContact: (orgId: string) => ["org-incident-contacts", { orgId }] as const
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchOrgIncidentContacts = async (orgId: string) => {
|
|
||||||
const { data } = await apiRequest.get<{ incidentContactsOrg: IncidentContact[] }>(
|
|
||||||
`/api/v1/organization/${orgId}/incidentContactOrg`
|
|
||||||
);
|
|
||||||
|
|
||||||
return data.incidentContactsOrg;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useGetOrgIncidentContact = (orgId: string) =>
|
export const useGetOrgIncidentContact = (orgId: string) =>
|
||||||
useQuery({
|
useQuery({
|
||||||
queryKey: incidentContactKeys.getAllContact(orgId),
|
queryKey: incidentContactKeys.getAllContact(orgId),
|
||||||
queryFn: () => fetchOrgIncidentContacts(orgId),
|
queryFn: async () => {
|
||||||
|
const { data } = await apiRequest.get<{ incidentContactsOrg: IncidentContact[] }>(
|
||||||
|
`/api/v1/organization/${orgId}/incidentContactOrg`
|
||||||
|
);
|
||||||
|
|
||||||
|
return data.incidentContactsOrg;
|
||||||
|
},
|
||||||
enabled: Boolean(orgId)
|
enabled: Boolean(orgId)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -32,4 +32,4 @@ export const useDeleteIntegration = () => {
|
|||||||
queryClient.invalidateQueries(workspaceKeys.getWorkspaceIntegrations(workspaceId));
|
queryClient.invalidateQueries(workspaceKeys.getWorkspaceIntegrations(workspaceId));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -41,8 +41,10 @@ export const useRenameOrg = () => {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation<{}, {}, RenameOrgDTO>({
|
return useMutation<{}, {}, RenameOrgDTO>({
|
||||||
mutationFn: ({ newOrgName, orgId }) =>
|
mutationFn: ({ newOrgName, orgId }) => {
|
||||||
apiRequest.patch(`/api/v1/organization/${orgId}/name`, { name: newOrgName }),
|
console.log("useRenameOrg");
|
||||||
|
return apiRequest.patch(`/api/v1/organization/${orgId}/name`, { name: newOrgName });
|
||||||
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries(organizationKeys.getUserOrganizations);
|
queryClient.invalidateQueries(organizationKeys.getUserOrganizations);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import {
|
|||||||
UserWsTags
|
UserWsTags
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
|
|
||||||
const workspaceTags = {
|
const workspaceTags = {
|
||||||
getWsTags: (workspaceID: string) => ["workspace-tags", { workspaceID }] as const
|
getWsTags: (workspaceID: string) => ["workspace-tags", { workspaceID }] as const
|
||||||
};
|
};
|
||||||
@@ -23,12 +22,13 @@ const fetchWsTag = async (workspaceID: string) => {
|
|||||||
return data.workspaceTags;
|
return data.workspaceTags;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetWsTags = (workspaceID: string) =>
|
export const useGetWsTags = (workspaceID: string) => {
|
||||||
useQuery({
|
return useQuery({
|
||||||
queryKey: workspaceTags.getWsTags(workspaceID),
|
queryKey: workspaceTags.getWsTags(workspaceID),
|
||||||
queryFn: () => fetchWsTag(workspaceID),
|
queryFn: () => fetchWsTag(workspaceID),
|
||||||
enabled: Boolean(workspaceID)
|
enabled: Boolean(workspaceID)
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export const useCreateWsTag = () => {
|
export const useCreateWsTag = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
@@ -59,4 +59,4 @@ export const useDeleteWsTag = () => {
|
|||||||
queryClient.invalidateQueries(workspaceTags.getWsTags(tagData?.workspace));
|
queryClient.invalidateQueries(workspaceTags.getWsTags(tagData?.workspace));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -3,10 +3,8 @@ export {
|
|||||||
useAddUserToOrg,
|
useAddUserToOrg,
|
||||||
useAddUserToWs,
|
useAddUserToWs,
|
||||||
useCreateAPIKey,
|
useCreateAPIKey,
|
||||||
useCreateMyAction,
|
|
||||||
useDeleteAPIKey,
|
useDeleteAPIKey,
|
||||||
useDeleteOrgMembership,
|
useDeleteOrgMembership,
|
||||||
useGetMyActions,
|
|
||||||
useGetMyAPIKeys,
|
useGetMyAPIKeys,
|
||||||
useGetMyIp,
|
useGetMyIp,
|
||||||
useGetMySessions,
|
useGetMySessions,
|
||||||
|
|||||||
@@ -158,8 +158,9 @@ export const useDeleteOrgMembership = () => {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation<{}, {}, DeletOrgMembershipDTO>({
|
return useMutation<{}, {}, DeletOrgMembershipDTO>({
|
||||||
mutationFn: ({ membershipId, orgId }) =>
|
mutationFn: ({ membershipId, orgId }) => {
|
||||||
apiRequest.delete(`/api/v2/organizations/${orgId}/memberships/${membershipId}`),
|
return apiRequest.delete(`/api/v2/organizations/${orgId}/memberships/${membershipId}`)
|
||||||
|
},
|
||||||
onSuccess: (_, { orgId }) => {
|
onSuccess: (_, { orgId }) => {
|
||||||
queryClient.invalidateQueries(userKeys.getOrgUsers(orgId));
|
queryClient.invalidateQueries(userKeys.getOrgUsers(orgId));
|
||||||
}
|
}
|
||||||
@@ -170,10 +171,11 @@ export const useUpdateOrgUserRole = () => {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation<{}, {}, UpdateOrgUserRoleDTO>({
|
return useMutation<{}, {}, UpdateOrgUserRoleDTO>({
|
||||||
mutationFn: ({ organizationId, membershipId, role }) =>
|
mutationFn: ({ organizationId, membershipId, role }) => {
|
||||||
apiRequest.patch(`/api/v2/organizations/${organizationId}/memberships/${membershipId}`, {
|
return apiRequest.patch(`/api/v2/organizations/${organizationId}/memberships/${membershipId}`, {
|
||||||
role
|
role
|
||||||
}),
|
});
|
||||||
|
},
|
||||||
onSuccess: (_, { organizationId }) => {
|
onSuccess: (_, { organizationId }) => {
|
||||||
queryClient.invalidateQueries(userKeys.getOrgUsers(organizationId));
|
queryClient.invalidateQueries(userKeys.getOrgUsers(organizationId));
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
export {
|
export {
|
||||||
|
useAddUserToWorkspace,
|
||||||
useCreateWorkspace,
|
useCreateWorkspace,
|
||||||
useCreateWsEnvironment,
|
useCreateWsEnvironment,
|
||||||
|
useDeleteUserFromWorkspace,
|
||||||
useDeleteWorkspace,
|
useDeleteWorkspace,
|
||||||
useDeleteWsEnvironment,
|
useDeleteWsEnvironment,
|
||||||
useGetUserWorkspaceMemberships,
|
useGetUserWorkspaceMemberships,
|
||||||
@@ -11,8 +13,9 @@ export {
|
|||||||
useGetWorkspaceIndexStatus,
|
useGetWorkspaceIndexStatus,
|
||||||
useGetWorkspaceIntegrations,
|
useGetWorkspaceIntegrations,
|
||||||
useGetWorkspaceSecrets,
|
useGetWorkspaceSecrets,
|
||||||
|
useGetWorkspaceUsers,
|
||||||
useNameWorkspaceSecrets,
|
useNameWorkspaceSecrets,
|
||||||
useRenameWorkspace,
|
useRenameWorkspace,
|
||||||
useToggleAutoCapitalization,
|
useToggleAutoCapitalization,
|
||||||
useUpdateWsEnvironment
|
useUpdateUserWorkspaceRole,
|
||||||
} from "./queries";
|
useUpdateWsEnvironment} from "./queries";
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ export const workspaceKeys = {
|
|||||||
getWorkspaceIntegrations: (workspaceId: string) => [{ workspaceId }, "workspace-integrations"],
|
getWorkspaceIntegrations: (workspaceId: string) => [{ workspaceId }, "workspace-integrations"],
|
||||||
getAllUserWorkspace: ["workspaces"] as const,
|
getAllUserWorkspace: ["workspaces"] as const,
|
||||||
getUserWsEnvironments: (workspaceId: string) => ["workspace-env", { workspaceId }] as const,
|
getUserWsEnvironments: (workspaceId: string) => ["workspace-env", { workspaceId }] as const,
|
||||||
getWorkspaceAuditLogs: (workspaceId: string) => [{ workspaceId }] as const
|
getWorkspaceAuditLogs: (workspaceId: string) => [{ workspaceId }] as const,
|
||||||
|
getWorkspaceUsers: (workspaceId: string) => [{ workspaceId }] as const
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchWorkspaceById = async (workspaceId: string) => {
|
const fetchWorkspaceById = async (workspaceId: string) => {
|
||||||
@@ -218,7 +219,9 @@ export const useDeleteWorkspace = () => {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation<{}, {}, DeleteWorkspaceDTO>({
|
return useMutation<{}, {}, DeleteWorkspaceDTO>({
|
||||||
mutationFn: ({ workspaceID }) => apiRequest.delete(`/api/v1/workspace/${workspaceID}`),
|
mutationFn: ({ workspaceID }) => {
|
||||||
|
return apiRequest.delete(`/api/v1/workspace/${workspaceID}`);
|
||||||
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries(workspaceKeys.getAllUserWorkspace);
|
queryClient.invalidateQueries(workspaceKeys.getAllUserWorkspace);
|
||||||
}
|
}
|
||||||
@@ -273,3 +276,74 @@ export const useDeleteWsEnvironment = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetWorkspaceUsers = (workspaceId: string) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: workspaceKeys.getWorkspaceUsers(workspaceId),
|
||||||
|
queryFn: async () => {
|
||||||
|
const { data: { users } } = await apiRequest.get(
|
||||||
|
`/api/v1/workspace/${workspaceId}/users`
|
||||||
|
);
|
||||||
|
return users;
|
||||||
|
},
|
||||||
|
enabled: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAddUserToWorkspace = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: async ({
|
||||||
|
email,
|
||||||
|
workspaceId
|
||||||
|
}: {
|
||||||
|
email: string;
|
||||||
|
workspaceId: string;
|
||||||
|
}) => {
|
||||||
|
const { data: { invitee, latestKey } } = await apiRequest.post(`/api/v1/workspace/${workspaceId}/invite-signup`, { email });
|
||||||
|
|
||||||
|
return ({
|
||||||
|
invitee,
|
||||||
|
latestKey
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onSuccess: (_, dto) => {
|
||||||
|
queryClient.invalidateQueries(workspaceKeys.getWorkspaceUsers(dto.workspaceId));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useDeleteUserFromWorkspace = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: async (membershipId: string) => {
|
||||||
|
const { data: { deletedMembership } } = await apiRequest.delete(`/api/v1/membership/${membershipId}`);
|
||||||
|
return deletedMembership;
|
||||||
|
},
|
||||||
|
onSuccess: (res) => {
|
||||||
|
queryClient.invalidateQueries(workspaceKeys.getWorkspaceUsers(res.workspace));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUpdateUserWorkspaceRole = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: async ({
|
||||||
|
membershipId,
|
||||||
|
role
|
||||||
|
}: {
|
||||||
|
membershipId: string;
|
||||||
|
role: string;
|
||||||
|
}) => {
|
||||||
|
const { data: { membership } } = await apiRequest.post(`/api/v1/membership/${membershipId}/change-role`, {
|
||||||
|
role
|
||||||
|
});
|
||||||
|
return membership;
|
||||||
|
},
|
||||||
|
onSuccess: (res) => {
|
||||||
|
queryClient.invalidateQueries(workspaceKeys.getWorkspaceUsers(res.workspace));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
integrationId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route deletes an integration from a certain project
|
|
||||||
* @param {*} integrationId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const deleteIntegration = ({ integrationId }: Props) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/integration/${integrationId}`, {
|
|
||||||
method: "DELETE",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
return (await res.json()).integration;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default deleteIntegration;
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
integrationAuthId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route deletes an integration authorization from a certain project
|
|
||||||
* @param {*} integrationAuthId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const deleteIntegrationAuth = ({ integrationAuthId }: Props) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/integration-auth/${integrationAuthId}`, {
|
|
||||||
method: "DELETE",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
return (await res.json()).integrationAuth;
|
|
||||||
}
|
|
||||||
console.log("Failed to delete an integration authorization");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default deleteIntegrationAuth;
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
integrationAuthId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getIntegrationApps = ({ integrationAuthId }: Props) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/integration-auth/${integrationAuthId}/apps`, {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
return (await res.json()).apps;
|
|
||||||
}
|
|
||||||
console.log("Failed to get available apps for an integration");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default getIntegrationApps;
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
const getIntegrationOptions = () =>
|
|
||||||
SecurityClient.fetchCall("/api/v1/integration-auth/integration-options", {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
return (await res.json()).integrationOptions;
|
|
||||||
}
|
|
||||||
console.log("Failed to get (cloud) integration options");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default getIntegrationOptions;
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
integrationId: string;
|
|
||||||
appName: string;
|
|
||||||
environment: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route starts the integration after teh default one if gonna set up.
|
|
||||||
* @param {*} integrationId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const startIntegration = ({ integrationId, appName, environment }: Props) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/integration/${integrationId}`, {
|
|
||||||
method: "PATCH",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
update: {
|
|
||||||
app: appName,
|
|
||||||
environment,
|
|
||||||
isActive: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
console.log("Failed to start an integration");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default startIntegration;
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
workspaceId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route gets authorizations of a certain project (Heroku, etc.)
|
|
||||||
* @param {*} workspaceId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const getWorkspaceAuthorizations = ({ workspaceId }: Props) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/workspace/${workspaceId}/authorizations`, {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
return (await res.json()).authorizations;
|
|
||||||
}
|
|
||||||
console.log("Failed to get project authorizations");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default getWorkspaceAuthorizations;
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
workspaceId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route gets integrations of a certain project (Heroku, etc.)
|
|
||||||
* @param {*} workspaceId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const getWorkspaceIntegrations = ({ workspaceId }: Props) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/workspace/${workspaceId}/integrations`, {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
return (await res.json()).integrations;
|
|
||||||
}
|
|
||||||
console.log("Failed to get the project integrations");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default getWorkspaceIntegrations;
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route starts the integration after teh default one if gonna set up.
|
|
||||||
* Update integration with id [integrationId] to sync envars from the project's
|
|
||||||
* [environment] to the integration [app] with active state [isActive]
|
|
||||||
* @param {Object} obj
|
|
||||||
* @param {String} obj.integrationId - id of integration
|
|
||||||
* @param {Boolean} obj.isActive - active state
|
|
||||||
* @param {String} obj.environment - project environment to push secrets from
|
|
||||||
* @param {String} obj.app - name of app
|
|
||||||
* @param {String} obj.appId - (optional) app ID for integration
|
|
||||||
* @param {String} obj.targetEnvironment - target environment for integration
|
|
||||||
* @param {String} obj.owner - (optional) owner login of repo for GitHub integration
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const updateIntegration = ({
|
|
||||||
integrationId,
|
|
||||||
isActive,
|
|
||||||
environment,
|
|
||||||
app,
|
|
||||||
appId,
|
|
||||||
targetEnvironment,
|
|
||||||
owner
|
|
||||||
}: {
|
|
||||||
integrationId: string;
|
|
||||||
isActive: boolean;
|
|
||||||
environment: string;
|
|
||||||
app: string;
|
|
||||||
appId: string | null;
|
|
||||||
targetEnvironment: string | null;
|
|
||||||
owner: string | null;
|
|
||||||
}) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/integration/${integrationId}`, {
|
|
||||||
method: "PATCH",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
app,
|
|
||||||
environment,
|
|
||||||
isActive,
|
|
||||||
appId,
|
|
||||||
targetEnvironment,
|
|
||||||
owner
|
|
||||||
})
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
return (await res.json()).integration;
|
|
||||||
}
|
|
||||||
console.log("Failed to start an integration");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default updateIntegration;
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route lets us get info about a certain org
|
|
||||||
* @param {string} orgId - the organization ID
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const getOrganization = ({ orgId }: { orgId: string }) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/organization/${orgId}`, {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res?.status === 200) {
|
|
||||||
return (await res.json()).organization;
|
|
||||||
}
|
|
||||||
console.log("Failed to get org info");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default getOrganization;
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route lets us get all the project memebrships of users in an org.
|
|
||||||
* @param {*} req
|
|
||||||
* @param {*} res
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
|
|
||||||
const getOrganizationProjectMemberships = (req: { orgId: string }) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/organization/${req.orgId}/workspace-memberships`, {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
return res.json();
|
|
||||||
}
|
|
||||||
console.log("Failed to get project memberships for users in an org");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default getOrganizationProjectMemberships;
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route lets us get all the users in an org.
|
|
||||||
* @param {*} req
|
|
||||||
* @param {*} res
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
|
|
||||||
// TODO: this file is not used anywhere
|
|
||||||
const getOrganizationProjects = (req: { orgId: string }) =>
|
|
||||||
SecurityClient.fetchCall(`/api/organization/${req.orgId}/workspaces`, {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
return (await res.json()).workspaces;
|
|
||||||
}
|
|
||||||
console.log("Failed to get projects for an org");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default getOrganizationProjects;
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route redirects the user to the right stripe billing page.
|
|
||||||
* @param {*} req
|
|
||||||
* @param {*} res
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const StripeRedirect = ({ orgId }: { orgId: string }) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/organization/${orgId}/customer-portal-session`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
window.location.href = (await res.json()).url;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log("Failed to redirect to Stripe");
|
|
||||||
});
|
|
||||||
|
|
||||||
export default StripeRedirect;
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route add an incident contact email to a certain organization
|
|
||||||
* @param {*} param0
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const addIncidentContact = (organizationId: string, email: string) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/organization/${organizationId}/incidentContactOrg`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
email
|
|
||||||
})
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
console.log("Failed to add an incident contact");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default addIncidentContact;
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function change the access of a user in a certain organization
|
|
||||||
* @param {string} organizationId
|
|
||||||
* @param {string} membershipId
|
|
||||||
* @param {string} role
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const changeUserRoleInOrganization = (organizationId: string, membershipId: string, role: string) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v2/organizations/${organizationId}/memberships/${membershipId}`, {
|
|
||||||
method: "PATCH",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
role
|
|
||||||
})
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
console.log("Failed to change the user role in an org");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default changeUserRoleInOrganization;
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route deletes an incident Contact from a certain organization
|
|
||||||
* @param {*} param0
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const deleteIncidentContact = (organizationId: string, email: string) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/organization/${organizationId}/incidentContactOrg`, {
|
|
||||||
method: "DELETE",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
email
|
|
||||||
})
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
console.log("Failed to delete an incident contact");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default deleteIncidentContact;
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function removes a certain member from a certain organization
|
|
||||||
* @param {*} membershipId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const deleteUserFromOrganization = (membershipId: string) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/membership-org/${membershipId}`, {
|
|
||||||
method: "DELETE",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
console.log("Failed to delete a user from an org");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default deleteUserFromOrganization;
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
export interface IIncidentContactOrg {
|
|
||||||
_id: string;
|
|
||||||
email: string;
|
|
||||||
organization: string;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* This routes gets all the incident contacts of a certain organization
|
|
||||||
* @param {*} workspaceId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const getIncidentContacts = (organizationId: string): Promise<IIncidentContactOrg[]> =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/organization/${organizationId}/incidentContactOrg`, {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
return (await res.json()).incidentContactsOrg;
|
|
||||||
}
|
|
||||||
console.log("Failed to get incident contacts");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default getIncidentContacts;
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route lets us rename a certain org.
|
|
||||||
* @param {*} req
|
|
||||||
* @param {*} res
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const renameOrg = (orgId: string, newOrgName: string) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/organization/${orgId}/name`, {
|
|
||||||
method: "PATCH",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
name: newOrgName
|
|
||||||
})
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
console.log("Failed to rename an organization");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default renameOrg;
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function adds a user to a project
|
|
||||||
* @param {*} email
|
|
||||||
* @param {*} workspaceId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const addUserToWorkspace = (email: string, workspaceId: string) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/workspace/${workspaceId}/invite-signup`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
email
|
|
||||||
})
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
return res.json();
|
|
||||||
}
|
|
||||||
console.log("Failed to add a user to project");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default addUserToWorkspace;
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function change the access of a user in a certain workspace
|
|
||||||
* @param {*} membershipId
|
|
||||||
* @param {*} role
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const changeUserRoleInWorkspace = (membershipId: string, role: string) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/membership/${membershipId}/change-role`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
role
|
|
||||||
})
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
console.log("Failed to change the user role in a project");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default changeUserRoleInWorkspace;
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function removes a certain member from a certain workspace
|
|
||||||
* @param {*} membershipId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const deleteUserFromWorkspace = (membershipId: string) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/membership/${membershipId}`, {
|
|
||||||
method: "DELETE",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
console.log("Failed to delete a user from a project");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default deleteUserFromWorkspace;
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route deletes a specified workspace.
|
|
||||||
* @param {*} workspaceId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const deleteWorkspace = (workspaceId: string) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/workspace/${workspaceId}`, {
|
|
||||||
method: "DELETE",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
console.log("Failed to delete a project");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default deleteWorkspace;
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route lets us get the public keys of everyone in your workspace.
|
|
||||||
* @param {string} workspaceId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const getWorkspaceKeys = ({ workspaceId }: { workspaceId: string }) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/workspace/${workspaceId}/keys`, {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res?.status === 200) {
|
|
||||||
return (await res.json()).publicKeys;
|
|
||||||
}
|
|
||||||
console.log("Failed to get the public keys of everyone in the workspace");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default getWorkspaceKeys;
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route lets us get the tags for a certain project
|
|
||||||
* @param {string} workspaceId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const getWorkspaceTags = ({ workspaceId }: { workspaceId: string }) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v2/workspace/${workspaceId}/tags`, {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res?.status === 200) {
|
|
||||||
return (await res.json()).workspaceTags;
|
|
||||||
}
|
|
||||||
console.log("Failed to get the tags available in a certain project");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default getWorkspaceTags;
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route lets us get all the users in the workspace.
|
|
||||||
* @param {string} workspaceId - workspace ID
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const getWorkspaceUsers = ({ workspaceId }: { workspaceId: string }) =>
|
|
||||||
SecurityClient.fetchCall(`/api/v1/workspace/${workspaceId}/users`, {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res?.status === 200) {
|
|
||||||
return (await res.json()).users;
|
|
||||||
}
|
|
||||||
console.log("Failed to get Project Users");
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default getWorkspaceUsers;
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
interface Workspace {
|
|
||||||
__v: number;
|
|
||||||
_id: string;
|
|
||||||
name: string;
|
|
||||||
autoCapitalization: boolean;
|
|
||||||
organization: string;
|
|
||||||
environments: Array<{ name: string; slug: string }>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This route lets us get the workspaces of a certain user
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const getWorkspaces = () =>
|
|
||||||
SecurityClient.fetchCall("/api/v1/workspace", {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res?.status === 200) {
|
|
||||||
const data = (await res.json()) as unknown as { workspaces: Workspace[] };
|
|
||||||
return data.workspaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error("Failed to get projects");
|
|
||||||
});
|
|
||||||
|
|
||||||
export default getWorkspaces;
|
|
||||||
@@ -11,15 +11,13 @@ import AddProjectMemberDialog from "@app/components/basic/dialog/AddProjectMembe
|
|||||||
import ProjectUsersTable from "@app/components/basic/table/ProjectUsersTable";
|
import ProjectUsersTable from "@app/components/basic/table/ProjectUsersTable";
|
||||||
import guidGenerator from "@app/components/utilities/randomId";
|
import guidGenerator from "@app/components/utilities/randomId";
|
||||||
import { Input } from "@app/components/v2";
|
import { Input } from "@app/components/v2";
|
||||||
import { useGetUser } from "@app/hooks/api";
|
import { useAddUserToWorkspace,useGetUser , useGetWorkspaceUsers } from "@app/hooks/api";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
decryptAssymmetric,
|
decryptAssymmetric,
|
||||||
encryptAssymmetric
|
encryptAssymmetric
|
||||||
} from "../../../../components/utilities/cryptography/crypto";
|
} from "../../../../components/utilities/cryptography/crypto";
|
||||||
import getOrganizationUsers from "../../../api/organization/GetOrgUsers";
|
import getOrganizationUsers from "../../../api/organization/GetOrgUsers";
|
||||||
import addUserToWorkspace from "../../../api/workspace/addUserToWorkspace";
|
|
||||||
import getWorkspaceUsers from "../../../api/workspace/getWorkspaceUsers";
|
|
||||||
import uploadKeys from "../../../api/workspace/uploadKeys";
|
import uploadKeys from "../../../api/workspace/uploadKeys";
|
||||||
|
|
||||||
interface UserProps {
|
interface UserProps {
|
||||||
@@ -42,7 +40,13 @@ interface MembershipProps {
|
|||||||
// #TODO: Update all the workspaceIds
|
// #TODO: Update all the workspaceIds
|
||||||
|
|
||||||
export default function Users() {
|
export default function Users() {
|
||||||
|
const router = useRouter();
|
||||||
|
const workspaceId = router.query.id as string;
|
||||||
|
|
||||||
const { data: user } = useGetUser();
|
const { data: user } = useGetUser();
|
||||||
|
const { data: workspaceUsers } = useGetWorkspaceUsers(workspaceId);
|
||||||
|
const { mutateAsync: addUserToWorkspaceMutateAsync } = useAddUserToWorkspace();
|
||||||
|
|
||||||
const [isAddOpen, setIsAddOpen] = useState(false);
|
const [isAddOpen, setIsAddOpen] = useState(false);
|
||||||
// let [isDeleteOpen, setIsDeleteOpen] = useState(false);
|
// let [isDeleteOpen, setIsDeleteOpen] = useState(false);
|
||||||
// let [userIdToBeDeleted, setUserIdToBeDeleted] = useState(false);
|
// let [userIdToBeDeleted, setUserIdToBeDeleted] = useState(false);
|
||||||
@@ -52,22 +56,16 @@ export default function Users() {
|
|||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const workspaceId = router.query.id as string;
|
|
||||||
|
|
||||||
const [userList, setUserList] = useState<any[]>([]);
|
const [userList, setUserList] = useState<any[]>([]);
|
||||||
const [isUserListLoading, setIsUserListLoading] = useState(true);
|
const [isUserListLoading, setIsUserListLoading] = useState(true);
|
||||||
const [orgUserList, setOrgUserList] = useState<any[]>([]);
|
const [orgUserList, setOrgUserList] = useState<any[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (user) {
|
if (user && workspaceUsers) {
|
||||||
(async () => {
|
(async () => {
|
||||||
setPersonalEmail(user.email);
|
setPersonalEmail(user.email);
|
||||||
|
|
||||||
// This part quiries the current users of a project
|
|
||||||
const workspaceUsers = await getWorkspaceUsers({
|
|
||||||
workspaceId
|
|
||||||
});
|
|
||||||
const tempUserList = workspaceUsers.map((membership: MembershipProps) => ({
|
const tempUserList = workspaceUsers.map((membership: MembershipProps) => ({
|
||||||
key: guidGenerator(),
|
key: guidGenerator(),
|
||||||
firstName: membership.user?.firstName,
|
firstName: membership.user?.firstName,
|
||||||
@@ -100,7 +98,7 @@ export default function Users() {
|
|||||||
);
|
);
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
}, [user]);
|
}, [user, workspaceUsers]);
|
||||||
|
|
||||||
const closeAddModal = () => {
|
const closeAddModal = () => {
|
||||||
setIsAddOpen(false);
|
setIsAddOpen(false);
|
||||||
@@ -123,7 +121,11 @@ export default function Users() {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
const submitAddModal = async () => {
|
const submitAddModal = async () => {
|
||||||
const result = await addUserToWorkspace(email, workspaceId);
|
const result = await addUserToWorkspaceMutateAsync({
|
||||||
|
email,
|
||||||
|
workspaceId
|
||||||
|
});
|
||||||
|
|
||||||
if (result?.invitee && result?.latestKey) {
|
if (result?.invitee && result?.latestKey) {
|
||||||
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY") as string;
|
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY") as string;
|
||||||
|
|
||||||
@@ -145,7 +147,6 @@ export default function Users() {
|
|||||||
}
|
}
|
||||||
setEmail("");
|
setEmail("");
|
||||||
setIsAddOpen(false);
|
setIsAddOpen(false);
|
||||||
router.reload();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return userList ? (
|
return userList ? (
|
||||||
|
|||||||
Reference in New Issue
Block a user