mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge remote-tracking branch 'origin/main' into ENG-3639
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import { components, OptionProps } from "react-select";
|
||||
import { faCheckCircle } from "@fortawesome/free-regular-svg-icons";
|
||||
import { faBuilding, faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { Badge, Tooltip } from "@app/components/v2";
|
||||
import { TAvailableAppConnection } from "@app/hooks/api/appConnections";
|
||||
|
||||
export const AppConnectionOption = ({
|
||||
isSelected,
|
||||
children,
|
||||
...props
|
||||
}: OptionProps<TAvailableAppConnection>) => {
|
||||
const isCreateOption = props.data.id === "_create";
|
||||
|
||||
return (
|
||||
<components.Option isSelected={isSelected} {...props}>
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
{isCreateOption ? (
|
||||
<div className="flex items-center gap-x-1 text-mineshaft-400">
|
||||
<FontAwesomeIcon icon={faPlus} size="sm" />
|
||||
<span className="mr-auto">Create New Connection</span>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<p className="truncate">{children}</p>
|
||||
{!props.data.projectId && (
|
||||
<Tooltip content="This connection belongs to your organization.">
|
||||
<div className="ml-2 mr-auto">
|
||||
<Badge className="flex h-5 w-min items-center gap-1 whitespace-nowrap bg-mineshaft-400/50 text-bunker-300 hover:text-bunker-300">
|
||||
<FontAwesomeIcon icon={faBuilding} size="sm" />
|
||||
Organization
|
||||
</Badge>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
{isSelected && (
|
||||
<FontAwesomeIcon className="ml-2 text-primary" icon={faCheckCircle} size="sm" />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</components.Option>
|
||||
);
|
||||
};
|
||||
1
frontend/src/components/app-connections/index.ts
Normal file
1
frontend/src/components/app-connections/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./AppConnectionOption";
|
||||
@@ -4,7 +4,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Link, useParams } from "@tanstack/react-router";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { useOrganization, useWorkspace } from "@app/context";
|
||||
import { useOrganization, useProject } from "@app/context";
|
||||
import { useToggle } from "@app/hooks";
|
||||
|
||||
import { createNotification } from "../notifications";
|
||||
@@ -51,7 +51,7 @@ export default function NavHeader({
|
||||
isProtectedBranch = false,
|
||||
protectionPolicyName
|
||||
}: Props): JSX.Element {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { currentProject } = useProject();
|
||||
const { currentOrg } = useOrganization();
|
||||
|
||||
const [isCopied, { timedToggle: toggleIsCopied }] = useToggle(false);
|
||||
@@ -79,7 +79,7 @@ export default function NavHeader({
|
||||
<>
|
||||
<FontAwesomeIcon icon={faAngleRight} className="ml-3 mr-3 text-xs text-gray-400" />
|
||||
<div className="truncate text-sm font-semibold text-bunker-300">
|
||||
{currentWorkspace?.name}
|
||||
{currentProject?.name}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
@@ -93,7 +93,7 @@ export default function NavHeader({
|
||||
{pageName === "Secrets" ? (
|
||||
<Link
|
||||
to="/projects/secret-management/$projectId/overview"
|
||||
params={{ projectId: currentWorkspace.id }}
|
||||
params={{ projectId: currentProject.id }}
|
||||
className="text-sm font-semibold text-primary/80 hover:text-primary"
|
||||
>
|
||||
{pageName}
|
||||
@@ -129,7 +129,7 @@ export default function NavHeader({
|
||||
<FontAwesomeIcon icon={faAngleRight} className="ml-3 mr-1.5 text-xs text-gray-400" />
|
||||
<Link
|
||||
to="/projects/secret-management/$projectId/secrets/$envSlug"
|
||||
params={{ projectId: currentWorkspace.id, envSlug: routerEnvSlug }}
|
||||
params={{ projectId: currentProject.id, envSlug: routerEnvSlug }}
|
||||
className="text-sm font-semibold text-primary/80 hover:text-primary"
|
||||
>
|
||||
{userAvailableEnvs?.find(({ slug }) => slug === currentEnv)?.name}
|
||||
@@ -192,7 +192,7 @@ export default function NavHeader({
|
||||
<Link
|
||||
to="/projects/secret-management/$projectId/secrets/$envSlug"
|
||||
params={{
|
||||
projectId: currentWorkspace.id,
|
||||
projectId: currentProject.id,
|
||||
envSlug: routerEnvSlug || ""
|
||||
}}
|
||||
search={(query) => ({ ...query, secretPath: newSecretPath })}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useFormContext, useWatch } from "react-hook-form";
|
||||
import { MongoAbility, MongoQuery } from "@casl/ability";
|
||||
import { Edge, Node, useEdgesState, useNodesState } from "@xyflow/react";
|
||||
|
||||
import { ProjectPermissionSub, useWorkspace } from "@app/context";
|
||||
import { ProjectPermissionSub, useProject } from "@app/context";
|
||||
import { ProjectPermissionSet } from "@app/context/ProjectPermissionContext";
|
||||
import { useListProjectEnvironmentsFolders } from "@app/hooks/api/secretFolders/queries";
|
||||
import { TSecretFolderWithPath } from "@app/hooks/api/secretFolders/types";
|
||||
@@ -36,15 +36,15 @@ export const useAccessTree = (
|
||||
searchPath: string,
|
||||
subject: ProjectPermissionSub
|
||||
) => {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { currentProject } = useProject();
|
||||
const { secretName, setSecretName, setViewMode, viewMode } = useAccessTreeContext();
|
||||
const { control } = useFormContext<AccessTreeForm>();
|
||||
const metadata = useWatch({ control, name: "metadata" });
|
||||
const [nodes, setNodes] = useNodesState<Node>([]);
|
||||
const [edges, setEdges] = useEdgesState<Edge>([]);
|
||||
const [environment, setEnvironment] = useState(currentWorkspace.environments[0]?.slug ?? "");
|
||||
const [environment, setEnvironment] = useState(currentProject.environments[0]?.slug ?? "");
|
||||
const { data: environmentsFolders, isPending } = useListProjectEnvironmentsFolders(
|
||||
currentWorkspace.id
|
||||
currentProject.id
|
||||
);
|
||||
|
||||
const [levelFolderMap, setLevelFolderMap] = useState<LevelFolderMap>({});
|
||||
@@ -279,7 +279,7 @@ export const useAccessTree = (
|
||||
environment,
|
||||
setEnvironment,
|
||||
isLoading: isPending,
|
||||
environments: currentWorkspace.environments,
|
||||
environments: currentProject.environments,
|
||||
secretName,
|
||||
setSecretName,
|
||||
viewMode,
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { FunctionComponent, ReactNode } from "react";
|
||||
import { BoundCanProps, Can } from "@casl/react";
|
||||
import { AbilityTuple, MongoAbility } from "@casl/ability";
|
||||
import { Can } from "@casl/react";
|
||||
|
||||
import { TooltipProps } from "@app/components/v2/Tooltip/Tooltip";
|
||||
import { TOrgPermission, useOrgPermission } from "@app/context/OrgPermissionContext";
|
||||
import { useOrgPermission } from "@app/context/OrgPermissionContext";
|
||||
import { OrgPermissionSet } from "@app/context/OrgPermissionContext/types";
|
||||
|
||||
import { AccessRestrictedBanner, Tooltip } from "../v2";
|
||||
|
||||
@@ -14,7 +16,7 @@ export const OrgPermissionGuardBanner = () => {
|
||||
);
|
||||
};
|
||||
|
||||
type Props = {
|
||||
type Props<T extends AbilityTuple> = {
|
||||
label?: ReactNode;
|
||||
// this prop is used when there exist already a tooltip as helper text for users
|
||||
// so when permission is allowed same tooltip will be reused to show helpertext
|
||||
@@ -22,9 +24,18 @@ type Props = {
|
||||
allowedLabel?: string;
|
||||
renderGuardBanner?: boolean;
|
||||
tooltipProps?: Omit<TooltipProps, "children">;
|
||||
} & BoundCanProps<TOrgPermission>;
|
||||
I: T[0];
|
||||
ability?: MongoAbility<T>;
|
||||
children: ReactNode | ((isAllowed: boolean, ability: T) => ReactNode);
|
||||
passThrough?: boolean;
|
||||
} & (
|
||||
| { an: T[1] }
|
||||
| {
|
||||
a: T[1];
|
||||
}
|
||||
);
|
||||
|
||||
export const OrgPermissionCan: FunctionComponent<Props> = ({
|
||||
export const OrgPermissionCan: FunctionComponent<Props<OrgPermissionSet>> = ({
|
||||
label = "Access restricted",
|
||||
children,
|
||||
passThrough = true,
|
||||
@@ -41,9 +52,7 @@ export const OrgPermissionCan: FunctionComponent<Props> = ({
|
||||
{(isAllowed, ability) => {
|
||||
// akhilmhdh: This is set as type due to error in casl react type.
|
||||
const finalChild =
|
||||
typeof children === "function"
|
||||
? children(isAllowed, ability as TOrgPermission)
|
||||
: children;
|
||||
typeof children === "function" ? children(isAllowed, ability as any) : children;
|
||||
|
||||
if (!isAllowed && passThrough) {
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { OrgPermissionCan } from "./OrgPermissionCan";
|
||||
import { ProjectPermissionCan } from "./ProjectPermissionCan";
|
||||
|
||||
interface PermissionCanProps {
|
||||
type: "project" | "org";
|
||||
I: any;
|
||||
a: any;
|
||||
children: (isAllowed: boolean, ability?: any) => React.ReactNode;
|
||||
}
|
||||
|
||||
export const VariablePermissionCan = ({ type, children, ...props }: PermissionCanProps) => {
|
||||
if (type === "project") {
|
||||
return <ProjectPermissionCan {...props}>{children}</ProjectPermissionCan>;
|
||||
}
|
||||
|
||||
return <OrgPermissionCan {...props}>{children}</OrgPermissionCan>;
|
||||
};
|
||||
@@ -3,3 +3,4 @@ export { GlobPermissionInfo } from "./GlobPermissionInfo";
|
||||
export { OrgPermissionCan } from "./OrgPermissionCan";
|
||||
export { PermissionDeniedBanner } from "./PermissionDeniedBanner";
|
||||
export { ProjectPermissionCan } from "./ProjectPermissionCan";
|
||||
export * from "./VariablePermissionCan";
|
||||
|
||||
@@ -6,7 +6,7 @@ import { z } from "zod";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import { Button, FormControl, Input, TextArea } from "@app/components/v2";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub, useProject } from "@app/context";
|
||||
import { useUpdateProject } from "@app/hooks/api";
|
||||
|
||||
const baseFormSchema = z.object({
|
||||
@@ -37,35 +37,35 @@ type Props = {
|
||||
};
|
||||
|
||||
export const ProjectOverviewChangeSection = ({ showSlugField = false }: Props) => {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { currentProject } = useProject();
|
||||
const { mutateAsync, isPending } = useUpdateProject();
|
||||
const { handleSubmit, control, reset, watch } = useForm<BaseFormData | FormDataWithSlug>({
|
||||
resolver: zodResolver(showSlugField ? formSchemaWithSlug : baseFormSchema)
|
||||
});
|
||||
|
||||
const currentSlug = showSlugField ? watch("slug") : currentWorkspace?.slug;
|
||||
const currentSlug = showSlugField ? watch("slug") : currentProject?.slug;
|
||||
|
||||
useEffect(() => {
|
||||
if (currentWorkspace) {
|
||||
if (currentProject) {
|
||||
reset({
|
||||
name: currentWorkspace.name,
|
||||
description: currentWorkspace.description ?? "",
|
||||
...(showSlugField && { slug: currentWorkspace.slug })
|
||||
name: currentProject.name,
|
||||
description: currentProject.description ?? "",
|
||||
...(showSlugField && { slug: currentProject.slug })
|
||||
});
|
||||
}
|
||||
}, [currentWorkspace, showSlugField]);
|
||||
}, [currentProject, showSlugField]);
|
||||
|
||||
const onFormSubmit = async (data: BaseFormData | FormDataWithSlug) => {
|
||||
try {
|
||||
if (!currentWorkspace?.id) return;
|
||||
if (!currentProject?.id) return;
|
||||
|
||||
await mutateAsync({
|
||||
projectID: currentWorkspace.id,
|
||||
projectId: currentProject.id,
|
||||
newProjectName: data.name,
|
||||
newProjectDescription: data.description,
|
||||
...(showSlugField &&
|
||||
"slug" in data && {
|
||||
newSlug: data.slug !== currentWorkspace.slug ? data.slug : undefined
|
||||
newSlug: data.slug !== currentProject.slug ? data.slug : undefined
|
||||
})
|
||||
});
|
||||
|
||||
@@ -105,7 +105,7 @@ export const ProjectOverviewChangeSection = ({ showSlugField = false }: Props) =
|
||||
variant="outline_bg"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(currentWorkspace?.id || "");
|
||||
navigator.clipboard.writeText(currentProject?.id || "");
|
||||
createNotification({
|
||||
text: "Copied project ID to clipboard",
|
||||
type: "success"
|
||||
|
||||
@@ -34,10 +34,10 @@ import {
|
||||
useUser
|
||||
} from "@app/context";
|
||||
import { getProjectHomePage, getProjectLottieIcon } from "@app/helpers/project";
|
||||
import { useCreateWorkspace, useGetExternalKmsList, useGetUserWorkspaces } from "@app/hooks/api";
|
||||
import { useCreateWorkspace, useGetExternalKmsList, useGetUserProjects } from "@app/hooks/api";
|
||||
import { INTERNAL_KMS_KEY_ID } from "@app/hooks/api/kms/types";
|
||||
import { ProjectType } from "@app/hooks/api/projects/types";
|
||||
import { InfisicalProjectTemplate, useListProjectTemplates } from "@app/hooks/api/projectTemplates";
|
||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z.string().trim().min(1, "Required").max(64, "Too long, maximum length is 64 characters"),
|
||||
@@ -89,7 +89,7 @@ const NewProjectForm = ({ onOpenChange }: NewProjectFormProps) => {
|
||||
const { permission } = useOrgPermission();
|
||||
const { user } = useUser();
|
||||
const createWs = useCreateWorkspace();
|
||||
const { refetch: refetchWorkspaces } = useGetUserWorkspaces();
|
||||
const { refetch: refetchWorkspaces } = useGetUserProjects();
|
||||
const { subscription } = useSubscription();
|
||||
|
||||
const canReadProjectTemplates = permission.can(
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Button, FormControl, Input, Modal, ModalClose, ModalContent } from "@app/components/v2";
|
||||
import { useRequestProjectAccess } from "@app/hooks/api";
|
||||
import { Project } from "@app/hooks/api/projects/types";
|
||||
|
||||
type ContentProps = {
|
||||
projectId: string;
|
||||
onComplete: () => void;
|
||||
};
|
||||
|
||||
const Content = ({ projectId, onComplete }: ContentProps) => {
|
||||
const form = useForm<{ note: string }>();
|
||||
|
||||
const requestProjectAccess = useRequestProjectAccess();
|
||||
|
||||
const onFormSubmit = ({ note }: { note: string }) => {
|
||||
if (requestProjectAccess.isPending) return;
|
||||
requestProjectAccess.mutate(
|
||||
{
|
||||
comment: note,
|
||||
projectId
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
createNotification({
|
||||
type: "success",
|
||||
title: "Project Access Request Sent",
|
||||
text: "Project admins will receive an email of your request"
|
||||
});
|
||||
onComplete();
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||
<FormControl label="Note">
|
||||
<Input {...form.register("note")} />
|
||||
</FormControl>
|
||||
<div className="mt-4 flex items-center">
|
||||
<Button className="mr-4" size="sm" type="submit" isLoading={form.formState.isSubmitting}>
|
||||
Submit Request
|
||||
</Button>
|
||||
<ModalClose asChild>
|
||||
<Button colorSchema="secondary" variant="plain">
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalClose>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
type RequestProjectAccessModalProps = {
|
||||
isOpen: boolean;
|
||||
onOpenChange: (isOpen: boolean) => void;
|
||||
project?: Project;
|
||||
onComplete?: () => void;
|
||||
};
|
||||
|
||||
export const RequestProjectAccessModal = ({
|
||||
isOpen,
|
||||
onOpenChange,
|
||||
project,
|
||||
onComplete
|
||||
}: RequestProjectAccessModalProps) => {
|
||||
if (!project) return null;
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
||||
<ModalContent
|
||||
title="Confirm Access Request"
|
||||
subTitle={`Requesting access to project ${project?.name}. You may include an optional note for project admins to review your request.`}
|
||||
>
|
||||
<Content
|
||||
onComplete={() => {
|
||||
onOpenChange(false);
|
||||
if (onComplete) onComplete();
|
||||
}}
|
||||
projectId={project?.id}
|
||||
/>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@@ -1 +1,2 @@
|
||||
export { NewProjectModal } from "./NewProjectModal";
|
||||
export * from "./RequestProjectAccessModal";
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useNavigate, useRouterState } from "@tanstack/react-router";
|
||||
|
||||
import { SecretRotationV2Form } from "@app/components/secret-rotations-v2/forms";
|
||||
import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms/schemas";
|
||||
import { SecretRotationV2ModalHeader } from "@app/components/secret-rotations-v2/SecretRotationV2ModalHeader";
|
||||
import { SecretRotationV2Select } from "@app/components/secret-rotations-v2/SecretRotationV2Select";
|
||||
import { Modal, ModalContent } from "@app/components/v2";
|
||||
import { ProjectEnv } from "@app/hooks/api/projects/types";
|
||||
import { SecretRotation, TSecretRotationV2 } from "@app/hooks/api/secretRotationsV2";
|
||||
import { WorkspaceEnv } from "@app/hooks/api/workspace/types";
|
||||
|
||||
type SharedProps = {
|
||||
secretPath: string;
|
||||
environment?: string;
|
||||
environments?: WorkspaceEnv[];
|
||||
environments?: ProjectEnv[];
|
||||
};
|
||||
|
||||
type Props = {
|
||||
@@ -24,14 +26,23 @@ type ContentProps = {
|
||||
onComplete: (secretRotation: TSecretRotationV2) => void;
|
||||
selectedRotation: SecretRotation | null;
|
||||
setSelectedRotation: (selectedRotation: SecretRotation | null) => void;
|
||||
initialFormData?: Partial<TSecretRotationV2Form>;
|
||||
onCancel: () => void;
|
||||
} & SharedProps;
|
||||
|
||||
const Content = ({ setSelectedRotation, selectedRotation, ...props }: ContentProps) => {
|
||||
const Content = ({
|
||||
setSelectedRotation,
|
||||
selectedRotation,
|
||||
initialFormData,
|
||||
onCancel,
|
||||
...props
|
||||
}: ContentProps) => {
|
||||
if (selectedRotation) {
|
||||
return (
|
||||
<SecretRotationV2Form
|
||||
onCancel={() => setSelectedRotation(null)}
|
||||
onCancel={onCancel}
|
||||
type={selectedRotation}
|
||||
initialFormData={initialFormData}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -42,12 +53,60 @@ const Content = ({ setSelectedRotation, selectedRotation, ...props }: ContentPro
|
||||
|
||||
export const CreateSecretRotationV2Modal = ({ onOpenChange, isOpen, ...props }: Props) => {
|
||||
const [selectedRotation, setSelectedRotation] = useState<SecretRotation | null>(null);
|
||||
const [initialFormData, setInitialFormData] = useState<Partial<TSecretRotationV2Form>>();
|
||||
|
||||
const {
|
||||
location: {
|
||||
search: { connectionId, connectionName, ...search },
|
||||
pathname
|
||||
}
|
||||
} = useRouterState();
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (connectionId && connectionName) {
|
||||
const storedFormData = localStorage.getItem("secretRotationFormData");
|
||||
|
||||
if (!storedFormData) return;
|
||||
|
||||
let form: Partial<TSecretRotationV2Form> = {};
|
||||
try {
|
||||
form = JSON.parse(storedFormData) as TSecretRotationV2Form;
|
||||
} catch {
|
||||
return;
|
||||
} finally {
|
||||
localStorage.removeItem("secretRotationFormData");
|
||||
}
|
||||
|
||||
onOpenChange(true);
|
||||
|
||||
setSelectedRotation(form.type ?? null);
|
||||
|
||||
setInitialFormData({
|
||||
...form,
|
||||
connection: { id: connectionId, name: connectionName }
|
||||
});
|
||||
|
||||
navigate({
|
||||
to: pathname,
|
||||
search
|
||||
});
|
||||
}
|
||||
}, [connectionId, connectionName]);
|
||||
|
||||
const handleReset = () => {
|
||||
setSelectedRotation(null);
|
||||
setInitialFormData(undefined);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) setSelectedRotation(null);
|
||||
if (!open) {
|
||||
handleReset();
|
||||
}
|
||||
onOpenChange(open);
|
||||
}}
|
||||
>
|
||||
@@ -84,9 +143,11 @@ export const CreateSecretRotationV2Modal = ({ onOpenChange, isOpen, ...props }:
|
||||
>
|
||||
<Content
|
||||
onComplete={() => {
|
||||
setSelectedRotation(null);
|
||||
handleReset();
|
||||
onOpenChange(false);
|
||||
}}
|
||||
onCancel={handleReset}
|
||||
initialFormData={initialFormData}
|
||||
selectedRotation={selectedRotation}
|
||||
setSelectedRotation={setSelectedRotation}
|
||||
{...props}
|
||||
|
||||
@@ -9,14 +9,14 @@ import {
|
||||
IS_ROTATION_DUAL_CREDENTIALS,
|
||||
SECRET_ROTATION_MAP
|
||||
} from "@app/helpers/secretRotationsV2";
|
||||
import { WorkspaceEnv } from "@app/hooks/api/workspace/types";
|
||||
import { ProjectEnv } from "@app/hooks/api/projects/types";
|
||||
|
||||
import { TSecretRotationV2Form } from "./schemas";
|
||||
import { SecretRotationV2ConnectionField } from "./SecretRotationV2ConnectionField";
|
||||
|
||||
type Props = {
|
||||
isUpdate: boolean;
|
||||
environments?: WorkspaceEnv[];
|
||||
environments?: ProjectEnv[];
|
||||
};
|
||||
|
||||
export const SecretRotationV2ConfigurationFields = ({ isUpdate, environments }: Props) => {
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import { Controller, useFormContext } from "react-hook-form";
|
||||
import { SingleValue } from "react-select";
|
||||
import { faInfoCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Link } from "@tanstack/react-router";
|
||||
|
||||
import { AppConnectionOption } from "@app/components/app-connections";
|
||||
import { FilterableSelect, FormControl } from "@app/components/v2";
|
||||
import { OrgPermissionSubjects, useOrgPermission } from "@app/context";
|
||||
import { OrgPermissionAppConnectionActions } from "@app/context/OrgPermissionContext/types";
|
||||
import { ProjectPermissionSub, useProject, useProjectPermission } from "@app/context";
|
||||
import { ProjectPermissionAppConnectionActions } from "@app/context/ProjectPermissionContext/types";
|
||||
import { APP_CONNECTION_MAP } from "@app/helpers/appConnections";
|
||||
import { SECRET_ROTATION_CONNECTION_MAP } from "@app/helpers/secretRotationsV2";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { useListAvailableAppConnections } from "@app/hooks/api/appConnections";
|
||||
import { AddAppConnectionModal } from "@app/pages/organization/AppConnections/AppConnectionsPage/components";
|
||||
|
||||
import { TSecretRotationV2Form } from "./schemas";
|
||||
|
||||
@@ -18,19 +21,26 @@ type Props = {
|
||||
};
|
||||
|
||||
export const SecretRotationV2ConnectionField = ({ onChange: callback, isUpdate }: Props) => {
|
||||
const { permission } = useOrgPermission();
|
||||
const { control, watch } = useFormContext<TSecretRotationV2Form>();
|
||||
const { permission } = useProjectPermission();
|
||||
const { control, watch, setValue } = useFormContext<TSecretRotationV2Form>();
|
||||
|
||||
const { popUp, handlePopUpToggle, handlePopUpOpen } = usePopUp(["addConnection"] as const);
|
||||
|
||||
const rotationType = watch("type");
|
||||
const app = SECRET_ROTATION_CONNECTION_MAP[rotationType];
|
||||
|
||||
const { data: availableConnections, isPending } = useListAvailableAppConnections(app);
|
||||
const { currentProject } = useProject();
|
||||
|
||||
const { data: availableConnections, isPending } = useListAvailableAppConnections(
|
||||
app,
|
||||
currentProject.id
|
||||
);
|
||||
|
||||
const connectionName = APP_CONNECTION_MAP[app].name;
|
||||
|
||||
const canCreateConnection = permission.can(
|
||||
OrgPermissionAppConnectionActions.Create,
|
||||
OrgPermissionSubjects.AppConnections
|
||||
ProjectPermissionAppConnectionActions.Create,
|
||||
ProjectPermissionSub.AppConnections
|
||||
);
|
||||
|
||||
const appName = APP_CONNECTION_MAP[app].name;
|
||||
@@ -66,37 +76,56 @@ export const SecretRotationV2ConnectionField = ({ onChange: callback, isUpdate }
|
||||
<FilterableSelect
|
||||
value={value}
|
||||
onChange={(newValue) => {
|
||||
if ((newValue as SingleValue<{ id: string; name: string }>)?.id === "_create") {
|
||||
handlePopUpOpen("addConnection");
|
||||
onChange(null);
|
||||
// store for oauth callback connections
|
||||
localStorage.setItem("secretRotationFormData", JSON.stringify(watch()));
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
|
||||
onChange(newValue);
|
||||
if (callback) callback();
|
||||
}}
|
||||
isLoading={isPending}
|
||||
options={availableConnections}
|
||||
options={[
|
||||
...(canCreateConnection ? [{ id: "_create", name: "Create Connection" }] : []),
|
||||
...(availableConnections ?? [])
|
||||
]}
|
||||
isDisabled={isUpdate}
|
||||
placeholder="Select connection..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.id}
|
||||
components={{ Option: AppConnectionOption }}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
control={control}
|
||||
name="connection"
|
||||
/>
|
||||
{!isUpdate && availableConnections?.length === 0 && (
|
||||
{!isUpdate && !isPending && !availableConnections?.length && !canCreateConnection && (
|
||||
<p className="-mt-2.5 mb-2.5 text-xs text-yellow">
|
||||
<FontAwesomeIcon className="mr-1" size="xs" icon={faInfoCircle} />
|
||||
{canCreateConnection ? (
|
||||
<>
|
||||
You do not have access to any {appName} Connections. Create one from the{" "}
|
||||
<Link to="/organization/app-connections" className="underline">
|
||||
App Connections
|
||||
</Link>{" "}
|
||||
page.
|
||||
</>
|
||||
) : (
|
||||
`You do not have access to any ${appName} Connections. Contact an admin to create one.`
|
||||
)}
|
||||
You do not have access to any {appName} Connections. Contact an admin to create one.
|
||||
</p>
|
||||
)}
|
||||
<AddAppConnectionModal
|
||||
isOpen={popUp.addConnection.isOpen}
|
||||
onOpenChange={(isOpen) => {
|
||||
// remove form storage, not oauth connection
|
||||
localStorage.removeItem("secretRotationFormData");
|
||||
handlePopUpToggle("addConnection", isOpen);
|
||||
}}
|
||||
projectType={currentProject.type}
|
||||
projectId={currentProject.id}
|
||||
app={app}
|
||||
onComplete={(connection) => {
|
||||
if (connection) {
|
||||
setValue("connection", connection);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -11,8 +11,9 @@ import { SecretRotationV2ParametersFields } from "@app/components/secret-rotatio
|
||||
import { SecretRotationV2ReviewFields } from "@app/components/secret-rotations-v2/forms/SecretRotationV2ReviewFields";
|
||||
import { SecretRotationV2SecretsMappingFields } from "@app/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields";
|
||||
import { Button } from "@app/components/v2";
|
||||
import { useWorkspace } from "@app/context";
|
||||
import { useProject } from "@app/context";
|
||||
import { IS_ROTATION_DUAL_CREDENTIALS, SECRET_ROTATION_MAP } from "@app/helpers/secretRotationsV2";
|
||||
import { ProjectEnv } from "@app/hooks/api/projects/types";
|
||||
import {
|
||||
SecretRotation,
|
||||
TSecretRotationV2,
|
||||
@@ -22,7 +23,6 @@ import {
|
||||
useCreateSecretRotationV2,
|
||||
useUpdateSecretRotationV2
|
||||
} from "@app/hooks/api/secretRotationsV2/mutations";
|
||||
import { WorkspaceEnv } from "@app/hooks/api/workspace/types";
|
||||
|
||||
import { SecretRotationV2FormSchema, TSecretRotationV2Form } from "./schemas";
|
||||
|
||||
@@ -32,8 +32,9 @@ type Props = {
|
||||
onCancel: () => void;
|
||||
secretPath: string;
|
||||
environment?: string;
|
||||
environments?: WorkspaceEnv[];
|
||||
environments?: ProjectEnv[];
|
||||
secretRotation?: TSecretRotationV2;
|
||||
initialFormData?: Partial<TSecretRotationV2Form>;
|
||||
};
|
||||
|
||||
const FORM_TABS: { name: string; key: string; fields: (keyof TSecretRotationV2Form)[] }[] = [
|
||||
@@ -64,11 +65,12 @@ export const SecretRotationV2Form = ({
|
||||
environment: envSlug,
|
||||
secretPath,
|
||||
secretRotation,
|
||||
environments
|
||||
environments,
|
||||
initialFormData
|
||||
}: Props) => {
|
||||
const createSecretRotation = useCreateSecretRotationV2();
|
||||
const updateSecretRotation = useUpdateSecretRotationV2();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { currentProject } = useProject();
|
||||
const { name: rotationType } = SECRET_ROTATION_MAP[type];
|
||||
|
||||
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
|
||||
@@ -80,7 +82,7 @@ export const SecretRotationV2Form = ({
|
||||
defaultValues: secretRotation
|
||||
? {
|
||||
...secretRotation,
|
||||
environment: currentWorkspace?.environments.find((env) => env.slug === envSlug),
|
||||
environment: currentProject?.environments.find((env) => env.slug === envSlug),
|
||||
secretPath
|
||||
}
|
||||
: {
|
||||
@@ -91,9 +93,10 @@ export const SecretRotationV2Form = ({
|
||||
hours: 0,
|
||||
minutes: 0
|
||||
},
|
||||
environment: currentWorkspace?.environments.find((env) => env.slug === envSlug),
|
||||
environment: currentProject?.environments.find((env) => env.slug === envSlug),
|
||||
secretPath,
|
||||
...(rotationOption!.template as object) // can't infer type since we don't know which specific type it is
|
||||
...((rotationOption?.template as object) ?? {}), // can't infer type since we don't know which specific type it is
|
||||
...(initialFormData as object)
|
||||
},
|
||||
reValidateMode: "onChange"
|
||||
});
|
||||
@@ -115,7 +118,7 @@ export const SecretRotationV2Form = ({
|
||||
|
||||
connectionId: connection.id,
|
||||
environment: environment.slug,
|
||||
projectId: currentWorkspace.id
|
||||
projectId: currentProject.id
|
||||
});
|
||||
try {
|
||||
const rotation = await mutation;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useNavigate, useRouterState } from "@tanstack/react-router";
|
||||
|
||||
import { TSecretScanningDataSourceForm } from "@app/components/secret-scanning/forms/schemas";
|
||||
import { Modal, ModalContent } from "@app/components/v2";
|
||||
import {
|
||||
SecretScanningDataSource,
|
||||
@@ -21,16 +23,19 @@ type ContentProps = {
|
||||
onComplete: (dataSource: TSecretScanningDataSource) => void;
|
||||
selectedDataSource: SecretScanningDataSource | null;
|
||||
setSelectedDataSource: (selectedDataSource: SecretScanningDataSource | null) => void;
|
||||
initialFormData?: Partial<TSecretScanningDataSourceForm>;
|
||||
onCancel: () => void;
|
||||
};
|
||||
|
||||
const Content = ({ setSelectedDataSource, selectedDataSource, ...props }: ContentProps) => {
|
||||
const Content = ({
|
||||
setSelectedDataSource,
|
||||
selectedDataSource,
|
||||
onCancel,
|
||||
...props
|
||||
}: ContentProps) => {
|
||||
if (selectedDataSource) {
|
||||
return (
|
||||
<SecretScanningDataSourceForm
|
||||
onCancel={() => setSelectedDataSource(null)}
|
||||
type={selectedDataSource}
|
||||
{...props}
|
||||
/>
|
||||
<SecretScanningDataSourceForm onCancel={onCancel} type={selectedDataSource} {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -41,12 +46,60 @@ export const CreateSecretScanningDataSourceModal = ({ onOpenChange, isOpen, ...p
|
||||
const [selectedDataSource, setSelectedDataSource] = useState<SecretScanningDataSource | null>(
|
||||
null
|
||||
);
|
||||
const [initialFormData, setInitialFormData] = useState<Partial<TSecretScanningDataSourceForm>>();
|
||||
|
||||
const {
|
||||
location: {
|
||||
search: { connectionId, connectionName, ...search },
|
||||
pathname
|
||||
}
|
||||
} = useRouterState();
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (connectionId && connectionName) {
|
||||
const storedFormData = localStorage.getItem("secretScanningDataSourceFormData");
|
||||
|
||||
if (!storedFormData) return;
|
||||
|
||||
let form: Partial<TSecretScanningDataSourceForm> = {};
|
||||
try {
|
||||
form = JSON.parse(storedFormData) as TSecretScanningDataSourceForm;
|
||||
} catch {
|
||||
return;
|
||||
} finally {
|
||||
localStorage.removeItem("secretScanningDataSourceFormData");
|
||||
}
|
||||
|
||||
onOpenChange(true);
|
||||
|
||||
setSelectedDataSource(form.type ?? null);
|
||||
|
||||
setInitialFormData({
|
||||
...form,
|
||||
connection: { id: connectionId, name: connectionName }
|
||||
});
|
||||
|
||||
navigate({
|
||||
to: pathname,
|
||||
search
|
||||
});
|
||||
}
|
||||
}, [connectionId, connectionName]);
|
||||
|
||||
const resetModal = () => {
|
||||
setSelectedDataSource(null);
|
||||
setInitialFormData(undefined);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) setSelectedDataSource(null);
|
||||
if (!open) {
|
||||
resetModal();
|
||||
}
|
||||
onOpenChange(open);
|
||||
}}
|
||||
>
|
||||
@@ -83,11 +136,13 @@ export const CreateSecretScanningDataSourceModal = ({ onOpenChange, isOpen, ...p
|
||||
>
|
||||
<Content
|
||||
onComplete={() => {
|
||||
setSelectedDataSource(null);
|
||||
resetModal();
|
||||
onOpenChange(false);
|
||||
}}
|
||||
onCancel={resetModal}
|
||||
selectedDataSource={selectedDataSource}
|
||||
setSelectedDataSource={setSelectedDataSource}
|
||||
initialFormData={initialFormData}
|
||||
{...props}
|
||||
/>
|
||||
</ModalContent>
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import { Controller, useFormContext } from "react-hook-form";
|
||||
import { SingleValue } from "react-select";
|
||||
import { faInfoCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Link } from "@tanstack/react-router";
|
||||
|
||||
import { AppConnectionOption } from "@app/components/app-connections";
|
||||
import { FilterableSelect, FormControl } from "@app/components/v2";
|
||||
import { OrgPermissionSubjects, useOrgPermission } from "@app/context";
|
||||
import { OrgPermissionAppConnectionActions } from "@app/context/OrgPermissionContext/types";
|
||||
import { ProjectPermissionSub, useProject, useProjectPermission } from "@app/context";
|
||||
import { ProjectPermissionAppConnectionActions } from "@app/context/ProjectPermissionContext/types";
|
||||
import { APP_CONNECTION_MAP } from "@app/helpers/appConnections";
|
||||
import { SECRET_SCANNING_DATA_SOURCE_CONNECTION_MAP } from "@app/helpers/secretScanningV2";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { useListAvailableAppConnections } from "@app/hooks/api/appConnections";
|
||||
import { AddAppConnectionModal } from "@app/pages/organization/AppConnections/AppConnectionsPage/components";
|
||||
|
||||
import { TSecretScanningDataSourceForm } from "./schemas";
|
||||
|
||||
@@ -21,19 +24,26 @@ export const SecretScanningDataSourceConnectionField = ({
|
||||
onChange: callback,
|
||||
isUpdate
|
||||
}: Props) => {
|
||||
const { permission } = useOrgPermission();
|
||||
const { control, watch } = useFormContext<TSecretScanningDataSourceForm>();
|
||||
const { permission } = useProjectPermission();
|
||||
const { control, watch, setValue } = useFormContext<TSecretScanningDataSourceForm>();
|
||||
|
||||
const { popUp, handlePopUpToggle, handlePopUpOpen } = usePopUp(["addConnection"] as const);
|
||||
|
||||
const dataSourceType = watch("type");
|
||||
const app = SECRET_SCANNING_DATA_SOURCE_CONNECTION_MAP[dataSourceType];
|
||||
|
||||
const { data: availableConnections, isPending } = useListAvailableAppConnections(app);
|
||||
const { currentProject } = useProject();
|
||||
|
||||
const { data: availableConnections, isPending } = useListAvailableAppConnections(
|
||||
app,
|
||||
currentProject.id
|
||||
);
|
||||
|
||||
const connectionName = APP_CONNECTION_MAP[app].name;
|
||||
|
||||
const canCreateConnection = permission.can(
|
||||
OrgPermissionAppConnectionActions.Create,
|
||||
OrgPermissionSubjects.AppConnections
|
||||
ProjectPermissionAppConnectionActions.Create,
|
||||
ProjectPermissionSub.AppConnections
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -67,37 +77,57 @@ export const SecretScanningDataSourceConnectionField = ({
|
||||
<FilterableSelect
|
||||
value={value}
|
||||
onChange={(newValue) => {
|
||||
if ((newValue as SingleValue<{ id: string; name: string }>)?.id === "_create") {
|
||||
handlePopUpOpen("addConnection");
|
||||
onChange(null);
|
||||
// store for oauth callback connections
|
||||
localStorage.setItem("secretScanningDataSourceFormData", JSON.stringify(watch()));
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
|
||||
onChange(newValue);
|
||||
if (callback) callback();
|
||||
}}
|
||||
isLoading={isPending}
|
||||
options={availableConnections}
|
||||
options={[
|
||||
...(canCreateConnection ? [{ id: "_create", name: "Create Connection" }] : []),
|
||||
...(availableConnections ?? [])
|
||||
]}
|
||||
isDisabled={isUpdate}
|
||||
placeholder="Select connection..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.id}
|
||||
components={{ Option: AppConnectionOption }}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
control={control}
|
||||
name="connection"
|
||||
/>
|
||||
{!isUpdate && availableConnections?.length === 0 && (
|
||||
{!isUpdate && !isPending && !availableConnections?.length && !canCreateConnection && (
|
||||
<p className="-mt-2.5 mb-2.5 text-xs text-yellow">
|
||||
<FontAwesomeIcon className="mr-1" size="xs" icon={faInfoCircle} />
|
||||
{canCreateConnection ? (
|
||||
<>
|
||||
You do not have access to any {connectionName} Connections. Create one from the{" "}
|
||||
<Link to="/organization/app-connections" className="underline">
|
||||
App Connections
|
||||
</Link>{" "}
|
||||
page.
|
||||
</>
|
||||
) : (
|
||||
`You do not have access to any ${connectionName} Connections. Contact an admin to create one.`
|
||||
)}
|
||||
You do not have access to any {connectionName} Connections. Contact an admin to create
|
||||
one.
|
||||
</p>
|
||||
)}
|
||||
<AddAppConnectionModal
|
||||
isOpen={popUp.addConnection.isOpen}
|
||||
onOpenChange={(isOpen) => {
|
||||
// remove form storage, not oauth connection
|
||||
localStorage.removeItem("secretScanningDataSourceFormData");
|
||||
handlePopUpToggle("addConnection", isOpen);
|
||||
}}
|
||||
projectType={currentProject.type}
|
||||
projectId={currentProject.id}
|
||||
app={app}
|
||||
onComplete={(connection) => {
|
||||
if (connection) {
|
||||
setValue("connection", connection);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Button } from "@app/components/v2";
|
||||
import { useWorkspace } from "@app/context";
|
||||
import { useProject } from "@app/context";
|
||||
import { SECRET_SCANNING_DATA_SOURCE_MAP } from "@app/helpers/secretScanningV2";
|
||||
import {
|
||||
SecretScanningDataSource,
|
||||
@@ -27,6 +27,7 @@ type Props = {
|
||||
type: SecretScanningDataSource;
|
||||
onCancel: () => void;
|
||||
dataSource?: TSecretScanningDataSource;
|
||||
initialFormData?: Partial<TSecretScanningDataSourceForm>;
|
||||
};
|
||||
|
||||
const FORM_TABS: { name: string; key: string; fields: (keyof TSecretScanningDataSourceForm)[] }[] =
|
||||
@@ -36,10 +37,16 @@ const FORM_TABS: { name: string; key: string; fields: (keyof TSecretScanningData
|
||||
{ name: "Review", key: "review", fields: [] }
|
||||
];
|
||||
|
||||
export const SecretScanningDataSourceForm = ({ type, onComplete, onCancel, dataSource }: Props) => {
|
||||
export const SecretScanningDataSourceForm = ({
|
||||
type,
|
||||
onComplete,
|
||||
onCancel,
|
||||
dataSource,
|
||||
initialFormData
|
||||
}: Props) => {
|
||||
const createDataSource = useCreateSecretScanningDataSource();
|
||||
const updateDataSource = useUpdateSecretScanningDataSource();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { currentProject } = useProject();
|
||||
const { name: sourceType } = SECRET_SCANNING_DATA_SOURCE_MAP[type];
|
||||
|
||||
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
|
||||
@@ -48,7 +55,8 @@ export const SecretScanningDataSourceForm = ({ type, onComplete, onCancel, dataS
|
||||
resolver: zodResolver(SecretScanningDataSourceSchema),
|
||||
defaultValues: dataSource ?? {
|
||||
type,
|
||||
isAutoScanEnabled: true // scott: this may need to be derived from type in the future
|
||||
isAutoScanEnabled: true, // scott: this may need to be derived from type in the future
|
||||
...(initialFormData as object)
|
||||
},
|
||||
reValidateMode: "onChange"
|
||||
});
|
||||
@@ -63,7 +71,7 @@ export const SecretScanningDataSourceForm = ({ type, onComplete, onCancel, dataS
|
||||
: createDataSource.mutateAsync({
|
||||
...formData,
|
||||
connectionId: connection?.id,
|
||||
projectId: currentWorkspace.id
|
||||
projectId: currentProject.id
|
||||
});
|
||||
try {
|
||||
const source = await mutation;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { TSecretSyncForm } from "@app/components/secret-syncs/forms/schemas";
|
||||
import { Modal, ModalContent } from "@app/components/v2";
|
||||
import { SecretSync, TSecretSync } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
@@ -11,18 +12,21 @@ type Props = {
|
||||
isOpen: boolean;
|
||||
onOpenChange: (isOpen: boolean) => void;
|
||||
selectSync?: SecretSync | null;
|
||||
initialFormData?: Partial<TSecretSyncForm>;
|
||||
};
|
||||
|
||||
type ContentProps = {
|
||||
onComplete: (secretSync: TSecretSync) => void;
|
||||
selectedSync: SecretSync | null;
|
||||
setSelectedSync: (selectedSync: SecretSync | null) => void;
|
||||
initialFormData?: Partial<TSecretSyncForm>;
|
||||
};
|
||||
|
||||
const Content = ({ onComplete, setSelectedSync, selectedSync }: ContentProps) => {
|
||||
const Content = ({ onComplete, setSelectedSync, selectedSync, initialFormData }: ContentProps) => {
|
||||
if (selectedSync) {
|
||||
return (
|
||||
<CreateSecretSyncForm
|
||||
initialFormData={initialFormData}
|
||||
onComplete={onComplete}
|
||||
onCancel={() => setSelectedSync(null)}
|
||||
destination={selectedSync}
|
||||
@@ -33,7 +37,12 @@ const Content = ({ onComplete, setSelectedSync, selectedSync }: ContentProps) =>
|
||||
return <SecretSyncSelect onSelect={setSelectedSync} />;
|
||||
};
|
||||
|
||||
export const CreateSecretSyncModal = ({ onOpenChange, selectSync = null, ...props }: Props) => {
|
||||
export const CreateSecretSyncModal = ({
|
||||
onOpenChange,
|
||||
selectSync = null,
|
||||
initialFormData,
|
||||
...props
|
||||
}: Props) => {
|
||||
const [selectedSync, setSelectedSync] = useState<SecretSync | null>(selectSync);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -67,6 +76,7 @@ export const CreateSecretSyncModal = ({ onOpenChange, selectSync = null, ...prop
|
||||
}}
|
||||
selectedSync={selectedSync}
|
||||
setSelectedSync={setSelectedSync}
|
||||
initialFormData={initialFormData}
|
||||
/>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
||||
@@ -8,7 +8,7 @@ import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Button, FormControl, Switch } from "@app/components/v2";
|
||||
import { useWorkspace } from "@app/context";
|
||||
import { useProject } from "@app/context";
|
||||
import { SECRET_SYNC_MAP } from "@app/helpers/secretSyncs";
|
||||
import {
|
||||
SecretSync,
|
||||
@@ -29,6 +29,7 @@ type Props = {
|
||||
onComplete: (secretSync: TSecretSync) => void;
|
||||
destination: SecretSync;
|
||||
onCancel: () => void;
|
||||
initialFormData?: Partial<TSecretSyncForm>;
|
||||
};
|
||||
|
||||
const FORM_TABS: { name: string; key: string; fields: (keyof TSecretSyncForm)[] }[] = [
|
||||
@@ -39,14 +40,20 @@ const FORM_TABS: { name: string; key: string; fields: (keyof TSecretSyncForm)[]
|
||||
{ name: "Review", key: "review", fields: [] }
|
||||
];
|
||||
|
||||
export const CreateSecretSyncForm = ({ destination, onComplete, onCancel }: Props) => {
|
||||
export const CreateSecretSyncForm = ({
|
||||
destination,
|
||||
onComplete,
|
||||
onCancel,
|
||||
initialFormData
|
||||
}: Props) => {
|
||||
const createSecretSync = useCreateSecretSync();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { currentProject } = useProject();
|
||||
const { name: destinationName } = SECRET_SYNC_MAP[destination];
|
||||
|
||||
const [showConfirmation, setShowConfirmation] = useState(false);
|
||||
|
||||
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
|
||||
// scoot: right now we only do this when creating a connection so we know index 1
|
||||
const [selectedTabIndex, setSelectedTabIndex] = useState(initialFormData ? 1 : 0);
|
||||
|
||||
const { syncOption } = useSecretSyncOption(destination);
|
||||
|
||||
@@ -59,7 +66,8 @@ export const CreateSecretSyncForm = ({ destination, onComplete, onCancel }: Prop
|
||||
initialSyncBehavior: syncOption?.canImportSecrets
|
||||
? undefined
|
||||
: SecretSyncInitialSyncBehavior.OverwriteDestination
|
||||
}
|
||||
},
|
||||
...initialFormData
|
||||
} as Partial<TSecretSyncForm>,
|
||||
reValidateMode: "onChange"
|
||||
});
|
||||
@@ -70,7 +78,7 @@ export const CreateSecretSyncForm = ({ destination, onComplete, onCancel }: Prop
|
||||
...formData,
|
||||
connectionId: connection.id,
|
||||
environment: environment.slug,
|
||||
projectId: currentWorkspace.id
|
||||
projectId: currentProject.id
|
||||
});
|
||||
|
||||
createNotification({
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import { Controller, useFormContext } from "react-hook-form";
|
||||
import { SingleValue } from "react-select";
|
||||
import { faInfoCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Link } from "@tanstack/react-router";
|
||||
|
||||
import { AppConnectionOption } from "@app/components/app-connections";
|
||||
import { FilterableSelect, FormControl } from "@app/components/v2";
|
||||
import { OrgPermissionSubjects, useOrgPermission } from "@app/context";
|
||||
import { OrgPermissionAppConnectionActions } from "@app/context/OrgPermissionContext/types";
|
||||
import { ProjectPermissionSub, useProject, useProjectPermission } from "@app/context";
|
||||
import { ProjectPermissionAppConnectionActions } from "@app/context/ProjectPermissionContext/types";
|
||||
import { APP_CONNECTION_MAP } from "@app/helpers/appConnections";
|
||||
import { SECRET_SYNC_CONNECTION_MAP } from "@app/helpers/secretSyncs";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { useListAvailableAppConnections } from "@app/hooks/api/appConnections";
|
||||
import { AddAppConnectionModal } from "@app/pages/organization/AppConnections/AppConnectionsPage/components";
|
||||
|
||||
import { TSecretSyncForm } from "./schemas";
|
||||
|
||||
@@ -17,19 +20,26 @@ type Props = {
|
||||
};
|
||||
|
||||
export const SecretSyncConnectionField = ({ onChange: callback }: Props) => {
|
||||
const { permission } = useOrgPermission();
|
||||
const { control, watch } = useFormContext<TSecretSyncForm>();
|
||||
const { permission } = useProjectPermission();
|
||||
const { control, watch, setValue } = useFormContext<TSecretSyncForm>();
|
||||
|
||||
const { popUp, handlePopUpToggle, handlePopUpOpen } = usePopUp(["addConnection"] as const);
|
||||
|
||||
const destination = watch("destination");
|
||||
const app = SECRET_SYNC_CONNECTION_MAP[destination];
|
||||
|
||||
const { data: availableConnections, isPending } = useListAvailableAppConnections(app);
|
||||
const { currentProject } = useProject();
|
||||
|
||||
const { data: availableConnections, isPending } = useListAvailableAppConnections(
|
||||
app,
|
||||
currentProject.id
|
||||
);
|
||||
|
||||
const connectionName = APP_CONNECTION_MAP[app].name;
|
||||
|
||||
const canCreateConnection = permission.can(
|
||||
OrgPermissionAppConnectionActions.Create,
|
||||
OrgPermissionSubjects.AppConnections
|
||||
ProjectPermissionAppConnectionActions.Create,
|
||||
ProjectPermissionSub.AppConnections
|
||||
);
|
||||
|
||||
const appName = APP_CONNECTION_MAP[SECRET_SYNC_CONNECTION_MAP[destination]].name;
|
||||
@@ -51,36 +61,55 @@ export const SecretSyncConnectionField = ({ onChange: callback }: Props) => {
|
||||
<FilterableSelect
|
||||
value={value}
|
||||
onChange={(newValue) => {
|
||||
if ((newValue as SingleValue<{ id: string; name: string }>)?.id === "_create") {
|
||||
handlePopUpOpen("addConnection");
|
||||
onChange(null);
|
||||
// store for oauth callback connections
|
||||
localStorage.setItem("secretSyncFormData", JSON.stringify(watch()));
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
|
||||
onChange(newValue);
|
||||
if (callback) callback();
|
||||
}}
|
||||
isLoading={isPending}
|
||||
options={availableConnections}
|
||||
options={[
|
||||
...(canCreateConnection ? [{ id: "_create", name: "Create Connection" }] : []),
|
||||
...(availableConnections ?? [])
|
||||
]}
|
||||
placeholder="Select connection..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.id}
|
||||
components={{ Option: AppConnectionOption }}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
control={control}
|
||||
name="connection"
|
||||
/>
|
||||
{availableConnections?.length === 0 && (
|
||||
{!isPending && !availableConnections?.length && !canCreateConnection && (
|
||||
<p className="-mt-2.5 mb-2.5 text-xs text-yellow">
|
||||
<FontAwesomeIcon className="mr-1" size="xs" icon={faInfoCircle} />
|
||||
{canCreateConnection ? (
|
||||
<>
|
||||
You do not have access to any {appName} Connections. Create one from the{" "}
|
||||
<Link to="/organization/app-connections" className="underline">
|
||||
App Connections
|
||||
</Link>{" "}
|
||||
page.
|
||||
</>
|
||||
) : (
|
||||
`You do not have access to any ${appName} Connections. Contact an admin to create one.`
|
||||
)}
|
||||
You do not have access to any {appName} Connections. Contact an admin to create one.
|
||||
</p>
|
||||
)}
|
||||
<AddAppConnectionModal
|
||||
isOpen={popUp.addConnection.isOpen}
|
||||
onOpenChange={(isOpen) => {
|
||||
// remove form storage, not oauth connection
|
||||
localStorage.removeItem("secretSyncFormData");
|
||||
handlePopUpToggle("addConnection", isOpen);
|
||||
}}
|
||||
projectType={currentProject.type}
|
||||
projectId={currentProject.id}
|
||||
app={app}
|
||||
onComplete={(connection) => {
|
||||
if (connection) {
|
||||
setValue("connection", connection);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ import { subject } from "@casl/ability";
|
||||
|
||||
import { FilterableSelect, FormControl } from "@app/components/v2";
|
||||
import { SecretPathInput } from "@app/components/v2/SecretPathInput";
|
||||
import { useProjectPermission, useWorkspace } from "@app/context";
|
||||
import { useProject, useProjectPermission } from "@app/context";
|
||||
import {
|
||||
ProjectPermissionSecretSyncActions,
|
||||
ProjectPermissionSub
|
||||
@@ -16,7 +16,7 @@ export const SecretSyncSourceFields = () => {
|
||||
const { control, watch, setError, clearErrors } = useFormContext<TSecretSyncForm>();
|
||||
|
||||
const { permission } = useProjectPermission();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { currentProject } = useProject();
|
||||
|
||||
const selectedEnvironment = watch("environment");
|
||||
const selectedSecretPath = watch("secretPath");
|
||||
@@ -48,7 +48,7 @@ export const SecretSyncSourceFields = () => {
|
||||
</p>
|
||||
|
||||
<Controller
|
||||
defaultValue={currentWorkspace.environments[0]}
|
||||
defaultValue={currentProject.environments[0]}
|
||||
control={control}
|
||||
name="environment"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
@@ -56,7 +56,7 @@ export const SecretSyncSourceFields = () => {
|
||||
<FilterableSelect
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
options={currentWorkspace.environments}
|
||||
options={currentProject.environments}
|
||||
placeholder="Select environment..."
|
||||
getOptionLabel={(option) => option?.name}
|
||||
getOptionValue={(option) => option?.id}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { FormControl, FormLabel, SecretInput, Spinner, Tooltip } from "@app/components/v2";
|
||||
import { useWorkspace } from "@app/context";
|
||||
import { useProject } from "@app/context";
|
||||
import { useGetSecretReferenceTree } from "@app/hooks/api";
|
||||
import { ApiErrorTypes, TApiErrors, TSecretReferenceTraceNode } from "@app/hooks/api/types";
|
||||
|
||||
@@ -89,8 +89,8 @@ export const SecretReferenceNode = ({
|
||||
};
|
||||
|
||||
export const SecretReferenceTree = ({ secretPath, environment, secretKey }: Props) => {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const projectId = currentWorkspace?.id || "";
|
||||
const { currentProject } = useProject();
|
||||
const projectId = currentProject?.id || "";
|
||||
|
||||
const { data, isPending, isError, error } = useGetSecretReferenceTree({
|
||||
secretPath,
|
||||
@@ -130,6 +130,14 @@ export const SecretReferenceTree = ({ secretPath, environment, secretKey }: Prop
|
||||
);
|
||||
}
|
||||
|
||||
if (tree?.children?.length === 0) {
|
||||
return (
|
||||
<div className="flex items-center justify-center py-4">
|
||||
<span className="text-mineshaft-400">This secret does not contain references</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FormControl label="Expanded value">
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
ModalContent,
|
||||
Tooltip
|
||||
} from "@app/components/v2";
|
||||
import { useWorkspace } from "@app/context";
|
||||
import { useProject } from "@app/context";
|
||||
import { useCreateWsTag } from "@app/hooks/api";
|
||||
import { slugSchema } from "@app/lib/schemas";
|
||||
|
||||
@@ -115,11 +115,10 @@ export const CreateTagModal = ({ isOpen, onToggle }: Props): JSX.Element => {
|
||||
}
|
||||
});
|
||||
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const workspaceId = currentWorkspace?.id || "";
|
||||
const { currentProject } = useProject();
|
||||
const projectId = currentProject?.id || "";
|
||||
|
||||
const { mutateAsync: createWsTag } = useCreateWsTag();
|
||||
|
||||
const [showHexInput, setShowHexInput] = useState<boolean>(false);
|
||||
const selectedTagColor = watch("color", secretTagsColors[0].hex);
|
||||
|
||||
@@ -130,7 +129,7 @@ export const CreateTagModal = ({ isOpen, onToggle }: Props): JSX.Element => {
|
||||
const onFormSubmit = async ({ slug, color }: FormData) => {
|
||||
try {
|
||||
await createWsTag({
|
||||
workspaceID: workspaceId,
|
||||
projectId,
|
||||
tagColor: color,
|
||||
tagSlug: slug
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import { faFolder, faKey, faLayerGroup, faSearch } from "@fortawesome/free-solid
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import * as Popover from "@radix-ui/react-popover";
|
||||
|
||||
import { useWorkspace } from "@app/context";
|
||||
import { useProject } from "@app/context";
|
||||
import { useDebounce, useToggle } from "@app/hooks";
|
||||
import { useGetProjectFolders, useGetProjectSecrets } from "@app/hooks/api";
|
||||
|
||||
@@ -55,6 +55,8 @@ type Props = Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, "onChange" | "val
|
||||
secretPath?: string;
|
||||
environment?: string;
|
||||
containerClassName?: string;
|
||||
isLoadingValue?: boolean;
|
||||
isErrorLoadingValue?: boolean;
|
||||
};
|
||||
|
||||
type ReferenceItem = {
|
||||
@@ -76,8 +78,8 @@ export const InfisicalSecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const workspaceId = currentWorkspace?.id || "";
|
||||
const { currentProject } = useProject();
|
||||
const projectId = currentProject?.id || "";
|
||||
|
||||
const [debouncedValue] = useDebounce(value, 100);
|
||||
|
||||
@@ -101,7 +103,7 @@ export const InfisicalSecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
||||
let predicate = suggestionSourceValue;
|
||||
if (isDeep) {
|
||||
const [envSlug, ...folderPaths] = suggestionSourceValue.split(".");
|
||||
const isValidEnvSlug = currentWorkspace?.environments.find((e) => e.slug === envSlug);
|
||||
const isValidEnvSlug = currentProject?.environments.find((e) => e.slug === envSlug);
|
||||
suggestionSourceEnv = isValidEnvSlug ? envSlug : undefined;
|
||||
suggestionSourceSecretPath = `/${folderPaths.slice(0, -1)?.join("/")}`;
|
||||
predicate = folderPaths[folderPaths.length - 1];
|
||||
@@ -125,7 +127,7 @@ export const InfisicalSecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
||||
viewSecretValue: false,
|
||||
environment: suggestionSource.environment || "",
|
||||
secretPath: suggestionSource.secretPath || "",
|
||||
workspaceId,
|
||||
projectId,
|
||||
options: {
|
||||
enabled: isPopupOpen
|
||||
}
|
||||
@@ -133,7 +135,7 @@ export const InfisicalSecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
||||
const { data: folders } = useGetProjectFolders({
|
||||
environment: suggestionSource.environment || "",
|
||||
path: suggestionSource.secretPath || "",
|
||||
projectId: workspaceId,
|
||||
projectId,
|
||||
options: {
|
||||
enabled: isPopupOpen
|
||||
}
|
||||
@@ -148,7 +150,7 @@ export const InfisicalSecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
||||
|
||||
if (!suggestionSource.isDeep) {
|
||||
// At first level only environments and secrets
|
||||
(currentWorkspace?.environments || []).forEach(({ name, slug }) => {
|
||||
(currentProject?.environments || []).forEach(({ name, slug }) => {
|
||||
if (name.toLowerCase().startsWith(predicate))
|
||||
suggestionsArr.push({
|
||||
label: name,
|
||||
@@ -185,7 +187,14 @@ export const InfisicalSecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
||||
}
|
||||
|
||||
return suggestionsArr;
|
||||
}, [secrets, folders, currentWorkspace?.environments, isPopupOpen, suggestionSource.predicate]);
|
||||
}, [
|
||||
secrets,
|
||||
folders,
|
||||
currentProject?.environments,
|
||||
isPopupOpen,
|
||||
suggestionSource.value,
|
||||
suggestionSource.predicate
|
||||
]);
|
||||
|
||||
const handleSuggestionSelect = (selectIndex?: number) => {
|
||||
const selectedSuggestion =
|
||||
@@ -307,11 +316,16 @@ export const InfisicalSecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
||||
ref={handleRef}
|
||||
onKeyDown={handleKeyDown}
|
||||
value={value}
|
||||
onFocus={() => setIsFocused.on()}
|
||||
onFocus={(evt) => {
|
||||
if (props.onFocus) props.onFocus(evt);
|
||||
setIsFocused.on();
|
||||
}}
|
||||
onBlur={(evt) => {
|
||||
// should not on blur when its mouse down selecting a item from suggestion
|
||||
if (!(evt.relatedTarget?.getAttribute("aria-label") === "suggestion-item"))
|
||||
setIsFocused.off();
|
||||
|
||||
if (props.onBlur) props.onBlur(evt);
|
||||
}}
|
||||
onChange={(e) => onChange?.(e.target.value)}
|
||||
containerClassName={containerClassName}
|
||||
|
||||
@@ -7,7 +7,16 @@ import { HIDDEN_SECRET_VALUE } from "@app/pages/secret-manager/SecretDashboardPa
|
||||
|
||||
const REGEX = /(\${([a-zA-Z0-9-_.]+)})/g;
|
||||
|
||||
const syntaxHighlight = (content?: string | null, isVisible?: boolean, isImport?: boolean) => {
|
||||
const syntaxHighlight = (
|
||||
content?: string | null,
|
||||
isVisible?: boolean,
|
||||
isImport?: boolean,
|
||||
isLoadingValue?: boolean,
|
||||
isErrorLoadingValue?: boolean
|
||||
) => {
|
||||
if (isLoadingValue) return HIDDEN_SECRET_VALUE;
|
||||
if (isErrorLoadingValue)
|
||||
return <span className="ph-no-capture text-red/75">Error loading secret value.</span>;
|
||||
if (isImport && !content) return "IMPORTED";
|
||||
if (content === "") return "EMPTY";
|
||||
if (!content) return "EMPTY";
|
||||
@@ -49,6 +58,8 @@ type Props = TextareaHTMLAttributes<HTMLTextAreaElement> & {
|
||||
isDisabled?: boolean;
|
||||
containerClassName?: string;
|
||||
canEditButNotView?: boolean;
|
||||
isLoadingValue?: boolean;
|
||||
isErrorLoadingValue?: boolean;
|
||||
};
|
||||
|
||||
const commonClassName = "font-mono text-sm caret-white border-none outline-none w-full break-all";
|
||||
@@ -66,6 +77,8 @@ export const SecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
||||
isReadOnly,
|
||||
onFocus,
|
||||
canEditButNotView,
|
||||
isLoadingValue,
|
||||
isErrorLoadingValue,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
@@ -84,7 +97,9 @@ export const SecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
||||
{syntaxHighlight(
|
||||
value,
|
||||
isVisible || (isSecretFocused && !valueAlwaysHidden),
|
||||
isImport
|
||||
isImport,
|
||||
isLoadingValue,
|
||||
isErrorLoadingValue
|
||||
)}
|
||||
</span>
|
||||
</code>
|
||||
@@ -115,7 +130,7 @@ export const SecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
||||
}}
|
||||
value={value || ""}
|
||||
{...props}
|
||||
readOnly={isReadOnly}
|
||||
readOnly={isReadOnly || isLoadingValue || isErrorLoadingValue}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import * as Popover from "@radix-ui/react-popover";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { useWorkspace } from "@app/context";
|
||||
import { useProject } from "@app/context";
|
||||
import { useDebounce } from "@app/hooks";
|
||||
import { useGetFoldersByEnv } from "@app/hooks/api";
|
||||
|
||||
@@ -35,12 +35,12 @@ export const SecretPathInput = ({
|
||||
const [highlightedIndex, setHighlightedIndex] = useState(-1);
|
||||
const [debouncedInputValue] = useDebounce(inputValue, 200);
|
||||
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const workspaceId = currentWorkspace?.id || "";
|
||||
const { currentProject } = useProject();
|
||||
const projectId = currentProject?.id || "";
|
||||
const { folderNames: folders } = useGetFoldersByEnv({
|
||||
path: secretPath,
|
||||
environments: [environment || currentWorkspace?.environments?.[0].slug || ""],
|
||||
projectId: workspaceId
|
||||
environments: [environment || currentProject?.environments?.[0].slug || ""],
|
||||
projectId
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MongoAbility } from "@casl/ability";
|
||||
import { ForcedSubject, MongoAbility } from "@casl/ability";
|
||||
|
||||
export enum OrgPermissionActions {
|
||||
Read = "read",
|
||||
@@ -21,6 +21,13 @@ export enum OrgGatewayPermissionActions {
|
||||
AttachGateways = "attach-gateways"
|
||||
}
|
||||
|
||||
export enum OrgRelayPermissionActions {
|
||||
CreateRelays = "create-relays",
|
||||
ListRelays = "list-relays",
|
||||
EditRelays = "edit-relays",
|
||||
DeleteRelays = "delete-relays"
|
||||
}
|
||||
|
||||
export enum OrgPermissionMachineIdentityAuthTemplateActions {
|
||||
ListTemplates = "list-templates",
|
||||
CreateTemplates = "create-templates",
|
||||
@@ -32,6 +39,7 @@ export enum OrgPermissionMachineIdentityAuthTemplateActions {
|
||||
|
||||
export enum OrgPermissionSubjects {
|
||||
Workspace = "workspace",
|
||||
Project = "project",
|
||||
Role = "role",
|
||||
Member = "member",
|
||||
Settings = "settings",
|
||||
@@ -50,6 +58,7 @@ export enum OrgPermissionSubjects {
|
||||
AppConnections = "app-connections",
|
||||
Kmip = "kmip",
|
||||
Gateway = "gateway",
|
||||
Relay = "relay",
|
||||
SecretShare = "secret-share",
|
||||
GithubOrgSync = "github-org-sync",
|
||||
GithubOrgSyncManual = "github-org-sync-manual",
|
||||
@@ -109,6 +118,7 @@ export type AppConnectionSubjectFields = {
|
||||
|
||||
export type OrgPermissionSet =
|
||||
| [OrgPermissionActions.Create, OrgPermissionSubjects.Workspace]
|
||||
| [OrgPermissionActions.Create, OrgPermissionSubjects.Project]
|
||||
| [OrgPermissionActions.Read, OrgPermissionSubjects.Workspace]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.Role]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.Member]
|
||||
@@ -126,7 +136,6 @@ export type OrgPermissionSet =
|
||||
| [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole]
|
||||
| [OrgPermissionAuditLogsActions, OrgPermissionSubjects.AuditLogs]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.ProjectTemplates]
|
||||
| [OrgPermissionAppConnectionActions, OrgPermissionSubjects.AppConnections]
|
||||
| [OrgPermissionIdentityActions, OrgPermissionSubjects.Identity]
|
||||
| [OrgPermissionKmipActions, OrgPermissionSubjects.Kmip]
|
||||
| [
|
||||
@@ -134,14 +143,14 @@ export type OrgPermissionSet =
|
||||
OrgPermissionSubjects.MachineIdentityAuthTemplate
|
||||
]
|
||||
| [OrgGatewayPermissionActions, OrgPermissionSubjects.Gateway]
|
||||
| [OrgPermissionSecretShareAction, OrgPermissionSubjects.SecretShare];
|
||||
// TODO(scott): add back once org UI refactored
|
||||
// | [
|
||||
// OrgPermissionAppConnectionActions,
|
||||
// (
|
||||
// | OrgPermissionSubjects.AppConnections
|
||||
// | (ForcedSubject<OrgPermissionSubjects.AppConnections> & AppConnectionSubjectFields)
|
||||
// )
|
||||
// ];
|
||||
| [OrgRelayPermissionActions, OrgPermissionSubjects.Relay]
|
||||
| [OrgPermissionSecretShareAction, OrgPermissionSubjects.SecretShare]
|
||||
| [
|
||||
OrgPermissionAppConnectionActions,
|
||||
(
|
||||
| OrgPermissionSubjects.AppConnections
|
||||
| (ForcedSubject<OrgPermissionSubjects.AppConnections> & AppConnectionSubjectFields)
|
||||
)
|
||||
];
|
||||
|
||||
export type TOrgPermission = MongoAbility<OrgPermissionSet>;
|
||||
|
||||
22
frontend/src/context/ProjectContext/ProjectContext.tsx
Normal file
22
frontend/src/context/ProjectContext/ProjectContext.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { useSuspenseQuery } from "@tanstack/react-query";
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
|
||||
import { projectKeys } from "@app/hooks/api";
|
||||
import { fetchProjectById } from "@app/hooks/api/projects/queries";
|
||||
|
||||
export const useProject = () => {
|
||||
const params = useParams({
|
||||
strict: false
|
||||
});
|
||||
if (!params.projectId) {
|
||||
throw new Error("Missing project id");
|
||||
}
|
||||
|
||||
const { data: currentProject } = useSuspenseQuery({
|
||||
queryKey: projectKeys.getProjectById(params.projectId),
|
||||
queryFn: () => fetchProjectById(params.projectId as string),
|
||||
staleTime: Infinity
|
||||
});
|
||||
|
||||
return { currentProject, projectId: currentProject.id };
|
||||
};
|
||||
1
frontend/src/context/ProjectContext/index.tsx
Normal file
1
frontend/src/context/ProjectContext/index.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export { useProject } from "./ProjectContext";
|
||||
@@ -22,8 +22,8 @@ export const useProjectPermission = () => {
|
||||
const {
|
||||
data: { permission, membership, assumedPrivilegeDetails }
|
||||
} = useSuspenseQuery({
|
||||
queryKey: roleQueryKeys.getUserProjectPermissions({ workspaceId: projectId }),
|
||||
queryFn: () => fetchUserProjectPermissions({ workspaceId: projectId }),
|
||||
queryKey: roleQueryKeys.getUserProjectPermissions({ projectId }),
|
||||
queryFn: () => fetchUserProjectPermissions({ projectId }),
|
||||
staleTime: Infinity,
|
||||
select: (data) => {
|
||||
const rule = unpackRules<RawRuleOf<MongoAbility<ProjectPermissionSet>>>(data.permissions);
|
||||
|
||||
@@ -154,6 +154,14 @@ export enum ProjectPermissionAuditLogsActions {
|
||||
Read = "read"
|
||||
}
|
||||
|
||||
export enum ProjectPermissionAppConnectionActions {
|
||||
Read = "read-app-connections",
|
||||
Create = "create-app-connections",
|
||||
Edit = "edit-app-connections",
|
||||
Delete = "delete-app-connections",
|
||||
Connect = "connect-app-connections"
|
||||
}
|
||||
|
||||
export enum PermissionConditionOperators {
|
||||
$IN = "$in",
|
||||
$ALL = "$all",
|
||||
@@ -173,6 +181,10 @@ export type IdentityManagementSubjectFields = {
|
||||
identityId: string;
|
||||
};
|
||||
|
||||
export type AppConnectionSubjectFields = {
|
||||
connectionId: string;
|
||||
};
|
||||
|
||||
export type ConditionalProjectPermissionSubject =
|
||||
| ProjectPermissionSub.SecretSyncs
|
||||
| ProjectPermissionSub.Secrets
|
||||
@@ -184,7 +196,8 @@ export type ConditionalProjectPermissionSubject =
|
||||
| ProjectPermissionSub.SecretFolders
|
||||
| ProjectPermissionSub.SecretImports
|
||||
| ProjectPermissionSub.SecretRotation
|
||||
| ProjectPermissionSub.SecretEvents;
|
||||
| ProjectPermissionSub.SecretEvents
|
||||
| ProjectPermissionSub.AppConnections;
|
||||
|
||||
export const formatedConditionsOperatorNames: { [K in PermissionConditionOperators]: string } = {
|
||||
[PermissionConditionOperators.$EQ]: "equal to",
|
||||
@@ -263,7 +276,8 @@ export enum ProjectPermissionSub {
|
||||
SecretScanningDataSources = "secret-scanning-data-sources",
|
||||
SecretScanningFindings = "secret-scanning-findings",
|
||||
SecretScanningConfigs = "secret-scanning-configs",
|
||||
SecretEvents = "secret-events"
|
||||
SecretEvents = "secret-events",
|
||||
AppConnections = "app-connections"
|
||||
}
|
||||
|
||||
export type SecretSubjectFields = {
|
||||
@@ -431,6 +445,13 @@ export type ProjectPermissionSet =
|
||||
| ProjectPermissionSub.SecretEvents
|
||||
| (ForcedSubject<ProjectPermissionSub.SecretEvents> & SecretEventSubjectFields)
|
||||
)
|
||||
]
|
||||
| [
|
||||
ProjectPermissionAppConnectionActions,
|
||||
(
|
||||
| ProjectPermissionSub.AppConnections
|
||||
| (ForcedSubject<ProjectPermissionSub.AppConnections> & AppConnectionSubjectFields)
|
||||
)
|
||||
];
|
||||
|
||||
export type TProjectPermission = MongoAbility<ProjectPermissionSet>;
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import { useSuspenseQuery } from "@tanstack/react-query";
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
|
||||
import { workspaceKeys } from "@app/hooks/api";
|
||||
import { fetchWorkspaceById } from "@app/hooks/api/workspace/queries";
|
||||
|
||||
export const useWorkspace = () => {
|
||||
const params = useParams({
|
||||
strict: false
|
||||
});
|
||||
if (!params.projectId) {
|
||||
throw new Error("Missing project id");
|
||||
}
|
||||
|
||||
const { data: currentWorkspace } = useSuspenseQuery({
|
||||
queryKey: workspaceKeys.getWorkspaceById(params.projectId),
|
||||
queryFn: () => fetchWorkspaceById(params.projectId as string),
|
||||
staleTime: Infinity
|
||||
});
|
||||
|
||||
return { currentWorkspace };
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export { useWorkspace } from "./WorkspaceContext";
|
||||
@@ -9,6 +9,7 @@ export {
|
||||
OrgPermissionSubjects,
|
||||
useOrgPermission
|
||||
} from "./OrgPermissionContext";
|
||||
export { useProject } from "./ProjectContext";
|
||||
export type { TProjectPermission } from "./ProjectPermissionContext";
|
||||
export {
|
||||
ProjectPermissionActions,
|
||||
@@ -29,4 +30,3 @@ export {
|
||||
export { useServerConfig } from "./ServerConfigContext";
|
||||
export { useSubscription } from "./SubscriptionContext";
|
||||
export { useUser } from "./UserContext";
|
||||
export { useWorkspace } from "./WorkspaceContext";
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
faServer,
|
||||
faUser
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { useRouterState } from "@tanstack/react-router";
|
||||
|
||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
import {
|
||||
@@ -221,3 +222,11 @@ export const AWS_REGIONS = [
|
||||
{ name: "AWS GovCloud (US-East)", slug: "us-gov-east-1" },
|
||||
{ name: "AWS GovCloud (US-West)", slug: "us-gov-west-1" }
|
||||
];
|
||||
|
||||
export const useGetAppConnectionOauthReturnUrl = () => {
|
||||
const {
|
||||
location: { pathname }
|
||||
} = useRouterState();
|
||||
|
||||
return pathname;
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@ export const AUDIT_LOG_STREAM_PROVIDER_MAP: Record<
|
||||
LogProvider,
|
||||
{ name: string; image?: string; icon?: IconDefinition; size?: number }
|
||||
> = {
|
||||
[LogProvider.Azure]: { name: "Azure", image: "Microsoft Azure.png", size: 60 },
|
||||
[LogProvider.Cribl]: { name: "Cribl", image: "Cribl.png", size: 60 },
|
||||
[LogProvider.Custom]: { name: "Custom", icon: faCode },
|
||||
[LogProvider.Datadog]: { name: "Datadog", image: "Datadog.png" },
|
||||
@@ -25,6 +26,8 @@ export function getProviderUrl(
|
||||
return logStream.credentials.url;
|
||||
case LogProvider.Splunk:
|
||||
return `https://${logStream.credentials.hostname}:8088/services/collector/event`;
|
||||
case LogProvider.Azure:
|
||||
return `${logStream.credentials.dceUrl}/dataCollectionRules/${logStream.credentials.dcrId}/streams/Custom-${logStream.credentials.cltName}_CL`;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unhandled provider in getProviderUrl: ${(logStream as TAuditLogStream).provider}`
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { apiRequest } from "@app/config/request";
|
||||
import { createWorkspace } from "@app/hooks/api/workspace/queries";
|
||||
import { ProjectType, WorkspaceEnv } from "@app/hooks/api/workspace/types";
|
||||
import { createWorkspace } from "@app/hooks/api/projects/queries";
|
||||
import { ProjectEnv, ProjectType } from "@app/hooks/api/projects/types";
|
||||
|
||||
const secretsToBeAdded = [
|
||||
{
|
||||
@@ -47,8 +47,8 @@ export const initProjectHelper = async ({ projectName }: { projectName: string }
|
||||
});
|
||||
|
||||
try {
|
||||
const { data } = await apiRequest.post("/api/v3/secrets/batch/raw", {
|
||||
workspaceId: project.id,
|
||||
const { data } = await apiRequest.post("/api/v4/secrets/batch", {
|
||||
projectId: project.id,
|
||||
environment: "dev",
|
||||
secretPath: "/",
|
||||
secrets: secretsToBeAdded
|
||||
@@ -74,7 +74,7 @@ export const getProjectBaseURL = (type: ProjectType) => {
|
||||
|
||||
// @ts-expect-error akhilmhdh: will remove this later
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export const getProjectHomePage = (type: ProjectType, environments: WorkspaceEnv[]) => {
|
||||
export const getProjectHomePage = (type: ProjectType, environments: ProjectEnv[]) => {
|
||||
switch (type) {
|
||||
case ProjectType.SecretManager:
|
||||
return "/projects/secret-management/$projectId/overview" as const;
|
||||
|
||||
@@ -16,8 +16,8 @@ import {
|
||||
export const accessApprovalKeys = {
|
||||
getAccessApprovalPolicies: (projectSlug: string) =>
|
||||
[{ projectSlug }, "access-approval-policies"] as const,
|
||||
getAccessApprovalPolicyOfABoard: (workspaceId: string, environment: string) =>
|
||||
[{ workspaceId, environment }, "access-approval-policy"] as const,
|
||||
getAccessApprovalPolicyOfABoard: (projectId: string, environment: string) =>
|
||||
[{ projectId, environment }, "access-approval-policy"] as const,
|
||||
|
||||
getAccessApprovalRequests: (
|
||||
projectSlug: string,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { EnforcementLevel, PolicyType } from "../policies/enums";
|
||||
import { ProjectEnv } from "../projects/types";
|
||||
import { TProjectPermission } from "../roles/types";
|
||||
import { ApprovalStatus } from "../secretApprovalRequest/types";
|
||||
import { WorkspaceEnv } from "../workspace/types";
|
||||
|
||||
export type TAccessApprovalPolicy = {
|
||||
id: string;
|
||||
@@ -9,7 +9,7 @@ export type TAccessApprovalPolicy = {
|
||||
approvals: number;
|
||||
secretPath: string;
|
||||
workspace: string;
|
||||
environments: WorkspaceEnv[];
|
||||
environments: ProjectEnv[];
|
||||
projectId: string;
|
||||
policyType: PolicyType;
|
||||
approversRequired: boolean;
|
||||
|
||||
@@ -20,7 +20,10 @@ export const useCreateAppConnection = () => {
|
||||
|
||||
return data.appConnection;
|
||||
},
|
||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: appConnectionKeys.list() })
|
||||
onSuccess: ({ projectId, app }) => {
|
||||
queryClient.invalidateQueries({ queryKey: appConnectionKeys.list(projectId) });
|
||||
queryClient.invalidateQueries({ queryKey: appConnectionKeys.listAvailable(app, projectId) });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -35,9 +38,10 @@ export const useUpdateAppConnection = () => {
|
||||
|
||||
return data.appConnection;
|
||||
},
|
||||
onSuccess: (_, { connectionId, app }) => {
|
||||
queryClient.invalidateQueries({ queryKey: appConnectionKeys.list() });
|
||||
queryClient.invalidateQueries({ queryKey: appConnectionKeys.byId(app, connectionId) });
|
||||
onSuccess: ({ projectId, app }) => {
|
||||
queryClient.invalidateQueries({ queryKey: appConnectionKeys.list(projectId) });
|
||||
queryClient.invalidateQueries({ queryKey: appConnectionKeys.listAvailable(app, projectId) });
|
||||
// queryClient.invalidateQueries({ queryKey: appConnectionKeys.byId(app, connectionId) });
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -50,9 +54,10 @@ export const useDeleteAppConnection = () => {
|
||||
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { connectionId, app }) => {
|
||||
queryClient.invalidateQueries({ queryKey: appConnectionKeys.list() });
|
||||
queryClient.invalidateQueries({ queryKey: appConnectionKeys.byId(app, connectionId) });
|
||||
onSuccess: ({ projectId, app }) => {
|
||||
queryClient.invalidateQueries({ queryKey: appConnectionKeys.list(projectId) });
|
||||
queryClient.invalidateQueries({ queryKey: appConnectionKeys.listAvailable(app, projectId) });
|
||||
// queryClient.invalidateQueries({ queryKey: appConnectionKeys.byId(app, connectionId) });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -5,29 +5,36 @@ import { apiRequest } from "@app/config/request";
|
||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
import {
|
||||
TAppConnection,
|
||||
TAppConnectionMap,
|
||||
TAppConnectionOptions,
|
||||
TAvailableAppConnection,
|
||||
TAvailableAppConnectionsResponse,
|
||||
TGetAppConnection,
|
||||
TListAppConnections
|
||||
} from "@app/hooks/api/appConnections/types";
|
||||
import {
|
||||
TAppConnectionOption,
|
||||
TAppConnectionOptionMap
|
||||
} from "@app/hooks/api/appConnections/types/app-options";
|
||||
import { ProjectType } from "@app/hooks/api/projects/types";
|
||||
|
||||
export const appConnectionKeys = {
|
||||
all: ["app-connection"] as const,
|
||||
options: () => [...appConnectionKeys.all, "options"] as const,
|
||||
list: () => [...appConnectionKeys.all, "list"] as const,
|
||||
listAvailable: (app: AppConnection) => [...appConnectionKeys.all, app, "list-available"] as const,
|
||||
listByApp: (app: AppConnection) => [...appConnectionKeys.list(), app],
|
||||
byId: (app: AppConnection, connectionId: string) =>
|
||||
[...appConnectionKeys.all, app, "by-id", connectionId] as const
|
||||
options: (projectType?: ProjectType) =>
|
||||
[...appConnectionKeys.all, "options", ...(projectType ? [projectType] : [])] as const,
|
||||
list: (projectId?: string | null) =>
|
||||
[...appConnectionKeys.all, "list", ...(projectId ? [projectId] : [])] as const,
|
||||
listAvailable: (app: AppConnection, projectId?: string | null) =>
|
||||
[...appConnectionKeys.all, app, "list-available", ...(projectId ? [projectId] : [])] as const
|
||||
// scott: may need these in the future but not using now
|
||||
// getUsage: (app: AppConnection, connectionId: string) =>
|
||||
// [...appConnectionKeys.all, "usage", app, connectionId] as const
|
||||
// listByApp: (app: AppConnection) => [...appConnectionKeys.list(), app],
|
||||
// scott: we will need this once we have individual app connection page
|
||||
// byId: (app: AppConnection, connectionId: string) =>
|
||||
// [...appConnectionKeys.all, app, "by-id", connectionId] as const
|
||||
};
|
||||
|
||||
export const useAppConnectionOptions = (
|
||||
projectType?: ProjectType,
|
||||
options?: Omit<
|
||||
UseQueryOptions<
|
||||
TAppConnectionOption[],
|
||||
@@ -39,10 +46,11 @@ export const useAppConnectionOptions = (
|
||||
>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: appConnectionKeys.options(),
|
||||
queryKey: appConnectionKeys.options(projectType),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<TAppConnectionOptions>(
|
||||
"/api/v1/app-connections/options"
|
||||
"/api/v1/app-connections/options",
|
||||
{ params: { projectType } }
|
||||
);
|
||||
|
||||
return data.appConnectionOptions;
|
||||
@@ -64,6 +72,7 @@ export const useGetAppConnectionOption = <T extends AppConnection>(app: T) => {
|
||||
};
|
||||
|
||||
export const useListAppConnections = (
|
||||
projectId?: string,
|
||||
options?: Omit<
|
||||
UseQueryOptions<
|
||||
TAppConnection[],
|
||||
@@ -75,10 +84,12 @@ export const useListAppConnections = (
|
||||
>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: appConnectionKeys.list(),
|
||||
queryKey: appConnectionKeys.list(projectId),
|
||||
queryFn: async () => {
|
||||
const { data } =
|
||||
await apiRequest.get<TListAppConnections<TAppConnection>>("/api/v1/app-connections");
|
||||
const { data } = await apiRequest.get<TListAppConnections<TAppConnection>>(
|
||||
"/api/v1/app-connections",
|
||||
{ params: { projectId } }
|
||||
);
|
||||
|
||||
return data.appConnections;
|
||||
},
|
||||
@@ -88,6 +99,7 @@ export const useListAppConnections = (
|
||||
|
||||
export const useListAvailableAppConnections = (
|
||||
app: AppConnection,
|
||||
projectId: string,
|
||||
options?: Omit<
|
||||
UseQueryOptions<
|
||||
TAvailableAppConnection[],
|
||||
@@ -99,10 +111,11 @@ export const useListAvailableAppConnections = (
|
||||
>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: appConnectionKeys.listAvailable(app),
|
||||
queryKey: appConnectionKeys.listAvailable(app, projectId),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<TAvailableAppConnectionsResponse>(
|
||||
`/api/v1/app-connections/${app}/available`
|
||||
`/api/v1/app-connections/${app}/available`,
|
||||
{ params: { projectId } }
|
||||
);
|
||||
|
||||
return data.appConnections;
|
||||
@@ -111,53 +124,82 @@ export const useListAvailableAppConnections = (
|
||||
});
|
||||
};
|
||||
|
||||
export const useListAppConnectionsByApp = <T extends AppConnection>(
|
||||
app: T,
|
||||
options?: Omit<
|
||||
UseQueryOptions<
|
||||
TAppConnectionMap[T][],
|
||||
unknown,
|
||||
TAppConnectionMap[T][],
|
||||
ReturnType<typeof appConnectionKeys.listByApp>
|
||||
>,
|
||||
"queryKey" | "queryFn"
|
||||
>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: appConnectionKeys.listByApp(app),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<TListAppConnections<TAppConnectionMap[T]>>(
|
||||
`/api/v1/app-connections/${app}`
|
||||
);
|
||||
// scott: may need these in the future but not using now
|
||||
// export const useGetAppConnectionUsageById = (
|
||||
// app: AppConnection,
|
||||
// connectionId: string,
|
||||
// options?: Omit<
|
||||
// UseQueryOptions<
|
||||
// AppConnectionUsage,
|
||||
// unknown,
|
||||
// AppConnectionUsage,
|
||||
// ReturnType<typeof appConnectionKeys.getUsage>
|
||||
// >,
|
||||
// "queryKey" | "queryFn"
|
||||
// >
|
||||
// ) => {
|
||||
// return useQuery({
|
||||
// queryKey: appConnectionKeys.getUsage(app, connectionId),
|
||||
// queryFn: async () => {
|
||||
// const { data } = await apiRequest.get<AppConnectionUsage>(
|
||||
// `/api/v1/app-connections/${app}/${connectionId}/usage`
|
||||
// );
|
||||
//
|
||||
// return data;
|
||||
// },
|
||||
// ...options
|
||||
// });
|
||||
// };
|
||||
|
||||
return data.appConnections;
|
||||
},
|
||||
...options
|
||||
});
|
||||
};
|
||||
// scott: may need these in the future but not using now
|
||||
// export const useListAppConnectionsByApp = <T extends AppConnection>(
|
||||
// app: T,
|
||||
// options?: Omit<
|
||||
// UseQueryOptions<
|
||||
// TAppConnectionMap[T][],
|
||||
// unknown,
|
||||
// TAppConnectionMap[T][],
|
||||
// ReturnType<typeof appConnectionKeys.listByApp>
|
||||
// >,
|
||||
// "queryKey" | "queryFn"
|
||||
// >
|
||||
// ) => {
|
||||
// return useQuery({
|
||||
// queryKey: appConnectionKeys.listByApp(app),
|
||||
// queryFn: async () => {
|
||||
// const { data } = await apiRequest.get<TListAppConnections<TAppConnectionMap[T]>>(
|
||||
// `/api/v1/app-connections/${app}`
|
||||
// );
|
||||
//
|
||||
// return data.appConnections;
|
||||
// },
|
||||
// ...options
|
||||
// });
|
||||
// };
|
||||
|
||||
export const useGetAppConnectionById = <T extends AppConnection>(
|
||||
app: T,
|
||||
connectionId: string,
|
||||
options?: Omit<
|
||||
UseQueryOptions<
|
||||
TAppConnectionMap[T],
|
||||
unknown,
|
||||
TAppConnectionMap[T],
|
||||
ReturnType<typeof appConnectionKeys.byId>
|
||||
>,
|
||||
"queryKey" | "queryFn"
|
||||
>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: appConnectionKeys.byId(app, connectionId),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<TGetAppConnection<TAppConnectionMap[T]>>(
|
||||
`/api/v1/app-connections/${app}/${connectionId}`
|
||||
);
|
||||
|
||||
return data.appConnection;
|
||||
},
|
||||
...options
|
||||
});
|
||||
};
|
||||
// scott: we will need this once we have individual app connection page
|
||||
// export const useGetAppConnectionById = <T extends AppConnection>(
|
||||
// app: T,
|
||||
// connectionId: string,
|
||||
// options?: Omit<
|
||||
// UseQueryOptions<
|
||||
// TAppConnectionMap[T],
|
||||
// unknown,
|
||||
// TAppConnectionMap[T],
|
||||
// ReturnType<typeof appConnectionKeys.byId>
|
||||
// >,
|
||||
// "queryKey" | "queryFn"
|
||||
// >
|
||||
// ) => {
|
||||
// return useQuery({
|
||||
// queryKey: appConnectionKeys.byId(app, connectionId),
|
||||
// queryFn: async () => {
|
||||
// const { data } = await apiRequest.get<TGetAppConnection<TAppConnectionMap[T]>>(
|
||||
// `/api/v1/app-connections/${app}/${connectionId}`
|
||||
// );
|
||||
//
|
||||
// return data.appConnection;
|
||||
// },
|
||||
// ...options
|
||||
// });
|
||||
// };
|
||||
|
||||
@@ -116,10 +116,11 @@ export type TAppConnection =
|
||||
| TNetlifyConnection
|
||||
| TOktaConnection;
|
||||
|
||||
export type TAvailableAppConnection = Pick<TAppConnection, "name" | "id">;
|
||||
export type TAvailableAppConnection = Pick<TAppConnection, "name" | "id" | "projectId">;
|
||||
|
||||
export type TListAppConnections<T extends TAppConnection> = { appConnections: T[] };
|
||||
export type TGetAppConnection<T extends TAppConnection> = { appConnection: T };
|
||||
// scott: we will need this once we have individual app connection page
|
||||
// export type TGetAppConnection<T extends TAppConnection> = { appConnection: T };
|
||||
export type TAppConnectionOptions = { appConnectionOptions: TAppConnectionOption[] };
|
||||
export type TAppConnectionResponse = { appConnection: TAppConnection };
|
||||
export type TAvailableAppConnectionsResponse = { appConnections: TAvailableAppConnection[] };
|
||||
@@ -133,6 +134,7 @@ export type TCreateAppConnectionDTO = Pick<
|
||||
| "description"
|
||||
| "isPlatformManagedCredentials"
|
||||
| "gatewayId"
|
||||
| "projectId"
|
||||
>;
|
||||
|
||||
export type TUpdateAppConnectionDTO = Partial<
|
||||
@@ -150,43 +152,60 @@ export type TDeleteAppConnectionDTO = {
|
||||
connectionId: string;
|
||||
};
|
||||
|
||||
export type TAppConnectionMap = {
|
||||
[AppConnection.AWS]: TAwsConnection;
|
||||
[AppConnection.GitHub]: TGitHubConnection;
|
||||
[AppConnection.GitHubRadar]: TGitHubRadarConnection;
|
||||
[AppConnection.GCP]: TGcpConnection;
|
||||
[AppConnection.AzureKeyVault]: TAzureKeyVaultConnection;
|
||||
[AppConnection.AzureAppConfiguration]: TAzureAppConfigurationConnection;
|
||||
[AppConnection.AzureClientSecrets]: TAzureClientSecretsConnection;
|
||||
[AppConnection.AzureDevOps]: TAzureDevOpsConnection;
|
||||
[AppConnection.AzureADCS]: TAzureADCSConnection;
|
||||
[AppConnection.Databricks]: TDatabricksConnection;
|
||||
[AppConnection.Humanitec]: THumanitecConnection;
|
||||
[AppConnection.TerraformCloud]: TTerraformCloudConnection;
|
||||
[AppConnection.Vercel]: TVercelConnection;
|
||||
[AppConnection.Postgres]: TPostgresConnection;
|
||||
[AppConnection.MsSql]: TMsSqlConnection;
|
||||
[AppConnection.MySql]: TMySqlConnection;
|
||||
[AppConnection.OracleDB]: TOracleDBConnection;
|
||||
[AppConnection.Camunda]: TCamundaConnection;
|
||||
[AppConnection.Windmill]: TWindmillConnection;
|
||||
[AppConnection.Auth0]: TAuth0Connection;
|
||||
[AppConnection.HCVault]: THCVaultConnection;
|
||||
[AppConnection.LDAP]: TLdapConnection;
|
||||
[AppConnection.TeamCity]: TTeamCityConnection;
|
||||
[AppConnection.OCI]: TOCIConnection;
|
||||
[AppConnection.OnePass]: TOnePassConnection;
|
||||
[AppConnection.Heroku]: THerokuConnection;
|
||||
[AppConnection.Render]: TRenderConnection;
|
||||
[AppConnection.Flyio]: TFlyioConnection;
|
||||
[AppConnection.GitLab]: TGitLabConnection;
|
||||
[AppConnection.Cloudflare]: TCloudflareConnection;
|
||||
[AppConnection.Bitbucket]: TBitbucketConnection;
|
||||
[AppConnection.Zabbix]: TZabbixConnection;
|
||||
[AppConnection.Railway]: TRailwayConnection;
|
||||
[AppConnection.Checkly]: TChecklyConnection;
|
||||
[AppConnection.Supabase]: TSupabaseConnection;
|
||||
[AppConnection.DigitalOcean]: TDigitalOceanConnection;
|
||||
[AppConnection.Netlify]: TNetlifyConnection;
|
||||
[AppConnection.Okta]: TOktaConnection;
|
||||
};
|
||||
// scott: we will need this once we have individual app connection page
|
||||
// export type TAppConnectionMap = {
|
||||
// [AppConnection.AWS]: TAwsConnection;
|
||||
// [AppConnection.GitHub]: TGitHubConnection;
|
||||
// [AppConnection.GitHubRadar]: TGitHubRadarConnection;
|
||||
// [AppConnection.GCP]: TGcpConnection;
|
||||
// [AppConnection.AzureKeyVault]: TAzureKeyVaultConnection;
|
||||
// [AppConnection.AzureAppConfiguration]: TAzureAppConfigurationConnection;
|
||||
// [AppConnection.AzureClientSecrets]: TAzureClientSecretsConnection;
|
||||
// [AppConnection.AzureDevOps]: TAzureDevOpsConnection;
|
||||
// [AppConnection.AzureADCS]: TAzureADCSConnection;
|
||||
// [AppConnection.Databricks]: TDatabricksConnection;
|
||||
// [AppConnection.Humanitec]: THumanitecConnection;
|
||||
// [AppConnection.TerraformCloud]: TTerraformCloudConnection;
|
||||
// [AppConnection.Vercel]: TVercelConnection;
|
||||
// [AppConnection.Postgres]: TPostgresConnection;
|
||||
// [AppConnection.MsSql]: TMsSqlConnection;
|
||||
// [AppConnection.MySql]: TMySqlConnection;
|
||||
// [AppConnection.OracleDB]: TOracleDBConnection;
|
||||
// [AppConnection.Camunda]: TCamundaConnection;
|
||||
// [AppConnection.Windmill]: TWindmillConnection;
|
||||
// [AppConnection.Auth0]: TAuth0Connection;
|
||||
// [AppConnection.HCVault]: THCVaultConnection;
|
||||
// [AppConnection.LDAP]: TLdapConnection;
|
||||
// [AppConnection.TeamCity]: TTeamCityConnection;
|
||||
// [AppConnection.OCI]: TOCIConnection;
|
||||
// [AppConnection.OnePass]: TOnePassConnection;
|
||||
// [AppConnection.Heroku]: THerokuConnection;
|
||||
// [AppConnection.Render]: TRenderConnection;
|
||||
// [AppConnection.Flyio]: TFlyioConnection;
|
||||
// [AppConnection.GitLab]: TGitLabConnection;
|
||||
// [AppConnection.Cloudflare]: TCloudflareConnection;
|
||||
// [AppConnection.Bitbucket]: TBitbucketConnection;
|
||||
// [AppConnection.Zabbix]: TZabbixConnection;
|
||||
// [AppConnection.Railway]: TRailwayConnection;
|
||||
// [AppConnection.Checkly]: TChecklyConnection;
|
||||
// [AppConnection.Supabase]: TSupabaseConnection;
|
||||
// [AppConnection.DigitalOcean]: TDigitalOceanConnection;
|
||||
// [AppConnection.Netlify]: TNetlifyConnection;
|
||||
// [AppConnection.Okta]: TOktaConnection;
|
||||
// };
|
||||
|
||||
// scott: we will need this once we have individual app connection page
|
||||
// export type AppConnectionUsage = {
|
||||
// projects: Array<{
|
||||
// id: string;
|
||||
// name: string;
|
||||
// slug: string;
|
||||
// type: ProjectType;
|
||||
// resources: {
|
||||
// secretSyncs: Array<{ id: string; name: string }>;
|
||||
// externalCas: Array<{ id: string; name: string }>;
|
||||
// secretRotations: Array<{ id: string; name: string }>;
|
||||
// dataSources: Array<{ id: string; name: string }>;
|
||||
// };
|
||||
// }>;
|
||||
// };
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { ProjectType } from "@app/hooks/api/projects/types";
|
||||
|
||||
export type TRootAppConnection = {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -8,4 +10,11 @@ export type TRootAppConnection = {
|
||||
updatedAt: string;
|
||||
isPlatformManagedCredentials?: boolean;
|
||||
gatewayId?: string | null;
|
||||
projectId?: string | null;
|
||||
project?: {
|
||||
name: string;
|
||||
type: ProjectType;
|
||||
slug: string;
|
||||
id: string;
|
||||
} | null;
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ export const useAssumeProjectPrivileges = () =>
|
||||
useMutation({
|
||||
mutationFn: async ({ projectId, actorId, actorType }: TProjectAssumePrivilegesDTO) => {
|
||||
const { data } = await apiRequest.post<{ message: string }>(
|
||||
`/api/v1/workspace/${projectId}/assume-privileges`,
|
||||
`/api/v1/projects/${projectId}/assume-privileges`,
|
||||
{ actorId, actorType }
|
||||
);
|
||||
|
||||
@@ -20,7 +20,7 @@ export const useRemoveAssumeProjectPrivilege = () =>
|
||||
useMutation({
|
||||
mutationFn: async ({ projectId }: { projectId: string }) => {
|
||||
const { data } = await apiRequest.delete<{ message: string }>(
|
||||
`/api/v1/workspace/${projectId}/assume-privileges`
|
||||
`/api/v1/projects/${projectId}/assume-privileges`
|
||||
);
|
||||
|
||||
return data;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export enum LogProvider {
|
||||
Azure = "azure",
|
||||
Cribl = "cribl",
|
||||
Custom = "custom",
|
||||
Datadog = "datadog",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { LogProvider } from "../enums";
|
||||
import { TAzureProviderLogStream } from "./providers/azure-provider";
|
||||
import { TCriblProviderLogStream } from "./providers/cribl-provider";
|
||||
import { TCustomProviderLogStream } from "./providers/custom-provider";
|
||||
import { TDatadogProviderLogStream } from "./providers/datadog-provider";
|
||||
@@ -8,9 +9,11 @@ export type TAuditLogStream =
|
||||
| TCustomProviderLogStream
|
||||
| TDatadogProviderLogStream
|
||||
| TSplunkProviderLogStream
|
||||
| TAzureProviderLogStream
|
||||
| TCriblProviderLogStream;
|
||||
|
||||
export type TAuditLogStreamProviderMap = {
|
||||
[LogProvider.Azure]: TAzureProviderLogStream;
|
||||
[LogProvider.Cribl]: TCriblProviderLogStream;
|
||||
[LogProvider.Custom]: TCustomProviderLogStream;
|
||||
[LogProvider.Datadog]: TDatadogProviderLogStream;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { LogProvider } from "../../enums";
|
||||
import { TRootProviderLogStream } from "./root-provider";
|
||||
|
||||
export type TAzureProviderLogStream = TRootProviderLogStream & {
|
||||
provider: LogProvider.Azure;
|
||||
credentials: {
|
||||
tenantId: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
dceUrl: string;
|
||||
dcrId: string;
|
||||
cltName: string;
|
||||
};
|
||||
};
|
||||
@@ -20,7 +20,7 @@ export const eventToNameMap: { [K in EventType]: string } = {
|
||||
[EventType.CREATE_SECRET]: "Create secret",
|
||||
[EventType.UPDATE_SECRET]: "Update secret",
|
||||
[EventType.DELETE_SECRET]: "Delete secret",
|
||||
[EventType.GET_WORKSPACE_KEY]: "Read project key",
|
||||
[EventType.GET_PROJECT_KEY]: "Read project key",
|
||||
[EventType.AUTHORIZE_INTEGRATION]: "Authorize integration",
|
||||
[EventType.UPDATE_INTEGRATION_AUTH]: "Update integration auth",
|
||||
[EventType.UNAUTHORIZE_INTEGRATION]: "Unauthorize integration",
|
||||
@@ -45,8 +45,8 @@ export const eventToNameMap: { [K in EventType]: string } = {
|
||||
[EventType.CREATE_ENVIRONMENT]: "Create environment",
|
||||
[EventType.UPDATE_ENVIRONMENT]: "Update environment",
|
||||
[EventType.DELETE_ENVIRONMENT]: "Delete environment",
|
||||
[EventType.ADD_WORKSPACE_MEMBER]: "Add member",
|
||||
[EventType.REMOVE_WORKSPACE_MEMBER]: "Remove member",
|
||||
[EventType.ADD_PROJECT_MEMBER]: "Add member",
|
||||
[EventType.REMOVE_PROJECT_MEMBER]: "Remove member",
|
||||
[EventType.CREATE_FOLDER]: "Create folder",
|
||||
[EventType.UPDATE_FOLDER]: "Update folder",
|
||||
[EventType.DELETE_FOLDER]: "Delete folder",
|
||||
@@ -58,8 +58,8 @@ export const eventToNameMap: { [K in EventType]: string } = {
|
||||
[EventType.CREATE_SECRET_IMPORT]: "Create secret import",
|
||||
[EventType.UPDATE_SECRET_IMPORT]: "Update secret import",
|
||||
[EventType.DELETE_SECRET_IMPORT]: "Delete secret import",
|
||||
[EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS]: "Update denied permissions",
|
||||
[EventType.UPDATE_USER_WORKSPACE_ROLE]: "Update user role",
|
||||
[EventType.UPDATE_USER_PROJECT_DENIED_PERMISSIONS]: "Update denied permissions",
|
||||
[EventType.UPDATE_USER_PROJECT_ROLE]: "Update user role",
|
||||
[EventType.CREATE_CA]: "Create CA",
|
||||
[EventType.GET_CA]: "Get CA",
|
||||
[EventType.UPDATE_CA]: "Update CA",
|
||||
@@ -132,6 +132,8 @@ export const eventToNameMap: { [K in EventType]: string } = {
|
||||
[EventType.CREATE_APP_CONNECTION]: "Create App Connection",
|
||||
[EventType.UPDATE_APP_CONNECTION]: "Update App Connection",
|
||||
[EventType.DELETE_APP_CONNECTION]: "Delete App Connection",
|
||||
[EventType.GET_APP_CONNECTION_USAGE]: "Get App Connection Usage",
|
||||
[EventType.MIGRATE_APP_CONNECTION]: "Migrate App Connection",
|
||||
[EventType.GET_SECRET_SYNCS]: "List secret syncs",
|
||||
[EventType.GET_SECRET_SYNC]: "Get Secret Sync",
|
||||
[EventType.CREATE_SECRET_SYNC]: "Create Secret Sync",
|
||||
@@ -192,6 +194,7 @@ export const eventToNameMap: { [K in EventType]: string } = {
|
||||
[EventType.UPDATE_IDENTITY_LDAP_AUTH]: "Updated LDAP Auth for identity",
|
||||
[EventType.GET_IDENTITY_LDAP_AUTH]: "Retrieved LDAP Auth for identity",
|
||||
[EventType.REVOKE_IDENTITY_LDAP_AUTH]: "Revoked LDAP Auth for identity",
|
||||
[EventType.CLEAR_IDENTITY_LDAP_AUTH_LOCKOUTS]: "Clear LDAP Auth lockouts",
|
||||
|
||||
[EventType.SECRET_SCANNING_DATA_SOURCE_LIST]: "List Secret Scanning Data Sources",
|
||||
[EventType.SECRET_SCANNING_DATA_SOURCE_CREATE]: "Create Secret Scanning Data Source",
|
||||
@@ -219,7 +222,23 @@ export const eventToNameMap: { [K in EventType]: string } = {
|
||||
[EventType.UPDATE_ORG]: "Update Organization",
|
||||
[EventType.CREATE_PROJECT]: "Create Project",
|
||||
[EventType.UPDATE_PROJECT]: "Update Project",
|
||||
[EventType.DELETE_PROJECT]: "Delete Project"
|
||||
[EventType.DELETE_PROJECT]: "Delete Project",
|
||||
|
||||
[EventType.CREATE_SECRET_REMINDER]: "Create Secret Reminder",
|
||||
[EventType.GET_SECRET_REMINDER]: "Get Secret Reminder",
|
||||
[EventType.DELETE_SECRET_REMINDER]: "Delete Secret Reminder",
|
||||
|
||||
[EventType.DASHBOARD_LIST_SECRETS]: "Dashboard List Secrets",
|
||||
[EventType.DASHBOARD_GET_SECRET_VALUE]: "Dashboard Get Secret Value",
|
||||
[EventType.DASHBOARD_GET_SECRET_VERSION_VALUE]: "Dashboard Get Secret Version Value",
|
||||
|
||||
[EventType.CREATE_PROJECT_ROLE]: "Create Project Role",
|
||||
[EventType.UPDATE_PROJECT_ROLE]: "Update Project Role",
|
||||
[EventType.DELETE_PROJECT_ROLE]: "Delete Project Role",
|
||||
|
||||
[EventType.CREATE_ORG_ROLE]: "Create Org Role",
|
||||
[EventType.UPDATE_ORG_ROLE]: "Update Org Role",
|
||||
[EventType.DELETE_ORG_ROLE]: "Delete Org Role"
|
||||
};
|
||||
|
||||
export const userAgentTypeToNameMap: { [K in UserAgentType]: string } = {
|
||||
|
||||
@@ -26,7 +26,7 @@ export enum EventType {
|
||||
CREATE_SECRET = "create-secret",
|
||||
UPDATE_SECRET = "update-secret",
|
||||
DELETE_SECRET = "delete-secret",
|
||||
GET_WORKSPACE_KEY = "get-workspace-key",
|
||||
GET_PROJECT_KEY = "get-project-key",
|
||||
AUTHORIZE_INTEGRATION = "authorize-integration",
|
||||
UPDATE_INTEGRATION_AUTH = "update-integration-auth",
|
||||
UNAUTHORIZE_INTEGRATION = "unauthorize-integration",
|
||||
@@ -54,12 +54,13 @@ export enum EventType {
|
||||
UPDATE_IDENTITY_LDAP_AUTH = "update-identity-ldap-auth",
|
||||
GET_IDENTITY_LDAP_AUTH = "get-identity-ldap-auth",
|
||||
REVOKE_IDENTITY_LDAP_AUTH = "revoke-identity-ldap-auth",
|
||||
CLEAR_IDENTITY_LDAP_AUTH_LOCKOUTS = "clear-identity-ldap-auth-lockouts",
|
||||
|
||||
CREATE_ENVIRONMENT = "create-environment",
|
||||
UPDATE_ENVIRONMENT = "update-environment",
|
||||
DELETE_ENVIRONMENT = "delete-environment",
|
||||
ADD_WORKSPACE_MEMBER = "add-workspace-member",
|
||||
REMOVE_WORKSPACE_MEMBER = "remove-workspace-member",
|
||||
ADD_PROJECT_MEMBER = "add-project-member",
|
||||
REMOVE_PROJECT_MEMBER = "remove-project-member",
|
||||
CREATE_FOLDER = "create-folder",
|
||||
UPDATE_FOLDER = "update-folder",
|
||||
DELETE_FOLDER = "delete-folder",
|
||||
@@ -71,8 +72,8 @@ export enum EventType {
|
||||
CREATE_SECRET_IMPORT = "create-secret-import",
|
||||
UPDATE_SECRET_IMPORT = "update-secret-import",
|
||||
DELETE_SECRET_IMPORT = "delete-secret-import",
|
||||
UPDATE_USER_WORKSPACE_ROLE = "update-user-workspace-role",
|
||||
UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS = "update-user-workspace-denied-permissions",
|
||||
UPDATE_USER_PROJECT_ROLE = "update-user-project-role",
|
||||
UPDATE_USER_PROJECT_DENIED_PERMISSIONS = "update-user-project-denied-permissions",
|
||||
CREATE_CA = "create-certificate-authority",
|
||||
GET_CA = "get-certificate-authority",
|
||||
UPDATE_CA = "update-certificate-authority",
|
||||
@@ -140,6 +141,8 @@ export enum EventType {
|
||||
CREATE_APP_CONNECTION = "create-app-connection",
|
||||
UPDATE_APP_CONNECTION = "update-app-connection",
|
||||
DELETE_APP_CONNECTION = "delete-app-connection",
|
||||
GET_APP_CONNECTION_USAGE = "get-app-connection-usage",
|
||||
MIGRATE_APP_CONNECTION = "migrate-app-connection",
|
||||
GET_SECRET_SYNCS = "get-secret-syncs",
|
||||
GET_SECRET_SYNC = "get-secret-sync",
|
||||
CREATE_SECRET_SYNC = "create-secret-sync",
|
||||
@@ -213,5 +216,21 @@ export enum EventType {
|
||||
|
||||
CREATE_PROJECT = "create-project",
|
||||
UPDATE_PROJECT = "update-project",
|
||||
DELETE_PROJECT = "delete-project"
|
||||
DELETE_PROJECT = "delete-project",
|
||||
|
||||
CREATE_SECRET_REMINDER = "create-secret-reminder",
|
||||
GET_SECRET_REMINDER = "get-secret-reminder",
|
||||
DELETE_SECRET_REMINDER = "delete-secret-reminder",
|
||||
|
||||
DASHBOARD_LIST_SECRETS = "dashboard-list-secrets",
|
||||
DASHBOARD_GET_SECRET_VALUE = "dashboard-get-secret-value",
|
||||
DASHBOARD_GET_SECRET_VERSION_VALUE = "dashboard-get-secret-version-value",
|
||||
|
||||
CREATE_PROJECT_ROLE = "create-project-role",
|
||||
UPDATE_PROJECT_ROLE = "update-project-role",
|
||||
DELETE_PROJECT_ROLE = "delete-project-role",
|
||||
|
||||
CREATE_ORG_ROLE = "create-org-role",
|
||||
UPDATE_ORG_ROLE = "update-org-role",
|
||||
DELETE_ORG_ROLE = "delete-org-role"
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import { TReactQueryOptions } from "@app/types/reactQuery";
|
||||
import { Actor, AuditLog, TGetAuditLogsFilter } from "./types";
|
||||
|
||||
export const auditLogKeys = {
|
||||
getAuditLogs: (workspaceId: string | null, filters: TGetAuditLogsFilter) =>
|
||||
[{ workspaceId, filters }, "audit-logs"] as const,
|
||||
getAuditLogActorFilterOpts: (workspaceId: string) =>
|
||||
[{ workspaceId }, "audit-log-actor-filters"] as const
|
||||
getAuditLogs: (projectId: string | null, filters: TGetAuditLogsFilter) =>
|
||||
[{ projectId, filters }, "audit-logs"] as const,
|
||||
getAuditLogActorFilterOpts: (projectId: string) =>
|
||||
[{ projectId }, "audit-log-actor-filters"] as const
|
||||
};
|
||||
|
||||
export const useGetAuditLogs = (
|
||||
@@ -56,12 +56,12 @@ export const useGetAuditLogs = (
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetAuditLogActorFilterOpts = (workspaceId: string) => {
|
||||
export const useGetAuditLogActorFilterOpts = (projectId: string) => {
|
||||
return useQuery({
|
||||
queryKey: auditLogKeys.getAuditLogActorFilterOpts(workspaceId),
|
||||
queryKey: auditLogKeys.getAuditLogActorFilterOpts(projectId),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<{ actors: Actor[] }>(
|
||||
`/api/v1/workspace/${workspaceId}/audit-logs/filters/actors`
|
||||
`/api/v1/projects/${projectId}/audit-logs/filters/actors`
|
||||
);
|
||||
return data.actors;
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ interface DeleteSecretEvent {
|
||||
}
|
||||
|
||||
interface GetWorkspaceKeyEvent {
|
||||
type: EventType.GET_WORKSPACE_KEY;
|
||||
type: EventType.GET_PROJECT_KEY;
|
||||
metadata: {
|
||||
keyId: string;
|
||||
};
|
||||
@@ -361,7 +361,7 @@ interface DeleteEnvironmentEvent {
|
||||
}
|
||||
|
||||
interface AddWorkspaceMemberEvent {
|
||||
type: EventType.ADD_WORKSPACE_MEMBER;
|
||||
type: EventType.ADD_PROJECT_MEMBER;
|
||||
metadata: {
|
||||
userId: string;
|
||||
email: string;
|
||||
@@ -369,7 +369,7 @@ interface AddWorkspaceMemberEvent {
|
||||
}
|
||||
|
||||
interface RemoveWorkspaceMemberEvent {
|
||||
type: EventType.REMOVE_WORKSPACE_MEMBER;
|
||||
type: EventType.REMOVE_PROJECT_MEMBER;
|
||||
metadata: {
|
||||
userId: string;
|
||||
email: string;
|
||||
@@ -500,7 +500,7 @@ interface DeleteSecretImportEvent {
|
||||
}
|
||||
|
||||
interface UpdateUserRole {
|
||||
type: EventType.UPDATE_USER_WORKSPACE_ROLE;
|
||||
type: EventType.UPDATE_USER_PROJECT_ROLE;
|
||||
metadata: {
|
||||
userId: string;
|
||||
email: string;
|
||||
@@ -510,7 +510,7 @@ interface UpdateUserRole {
|
||||
}
|
||||
|
||||
interface UpdateUserDeniedPermissions {
|
||||
type: EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS;
|
||||
type: EventType.UPDATE_USER_PROJECT_DENIED_PERMISSIONS;
|
||||
metadata: {
|
||||
userId: string;
|
||||
email: string;
|
||||
@@ -874,6 +874,13 @@ interface IntegrationSyncedEvent {
|
||||
};
|
||||
}
|
||||
|
||||
interface ClearIdentityLdapAuthLockoutsEvent {
|
||||
type: EventType.CLEAR_IDENTITY_LDAP_AUTH_LOCKOUTS;
|
||||
metadata: {
|
||||
identityId: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type Event =
|
||||
| GetSecretsEvent
|
||||
| GetSecretEvent
|
||||
@@ -958,7 +965,8 @@ export type Event =
|
||||
| GetCertificateTemplateEstConfig
|
||||
| UpdateProjectWorkflowIntegrationConfig
|
||||
| GetProjectWorkflowIntegrationConfig
|
||||
| IntegrationSyncedEvent;
|
||||
| IntegrationSyncedEvent
|
||||
| ClearIdentityLdapAuthLockoutsEvent;
|
||||
|
||||
export type AuditLog = {
|
||||
id: string;
|
||||
|
||||
@@ -5,8 +5,8 @@ import { apiRequest } from "@app/config/request";
|
||||
import { SessionStorageKeys } from "@app/const";
|
||||
|
||||
import { organizationKeys } from "../organization/queries";
|
||||
import { projectKeys } from "../projects";
|
||||
import { setAuthToken } from "../reactQuery";
|
||||
import { workspaceKeys } from "../workspace";
|
||||
import {
|
||||
CompleteAccountDTO,
|
||||
CompleteAccountSignupDTO,
|
||||
@@ -101,7 +101,7 @@ export const useSelectOrganization = () => {
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [organizationKeys.getUserOrganizations, workspaceKeys.getAllUserWorkspace]
|
||||
queryKey: [organizationKeys.getUserOrganizations, projectKeys.getAllUserProjects]
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { workspaceKeys } from "../workspace";
|
||||
import { projectKeys } from "../projects";
|
||||
import { CaType } from "./enums";
|
||||
import { caKeys } from "./queries";
|
||||
import {
|
||||
@@ -118,8 +118,8 @@ export const useImportCaCertificate = (projectId: string) => {
|
||||
);
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { caId, projectSlug }) => {
|
||||
queryClient.invalidateQueries({ queryKey: workspaceKeys.getWorkspaceCas({ projectSlug }) });
|
||||
onSuccess: (_, { caId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: projectKeys.getProjectCas({ projectId }) });
|
||||
queryClient.invalidateQueries({ queryKey: caKeys.getCaCerts(caId) });
|
||||
queryClient.invalidateQueries({ queryKey: caKeys.getCaCert(caId) });
|
||||
queryClient.invalidateQueries({
|
||||
@@ -142,7 +142,7 @@ export const useCreateCertificate = () => {
|
||||
},
|
||||
onSuccess: (_, { projectSlug }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.forWorkspaceCertificates(projectSlug)
|
||||
queryKey: projectKeys.forProjectCertificates(projectSlug)
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -158,8 +158,8 @@ export const useRenewCa = () => {
|
||||
);
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { caId, projectSlug }) => {
|
||||
queryClient.invalidateQueries({ queryKey: workspaceKeys.getWorkspaceCas({ projectSlug }) });
|
||||
onSuccess: ({ projectId }, { caId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: projectKeys.getProjectCas({ projectId }) });
|
||||
queryClient.invalidateQueries({ queryKey: caKeys.getCaById(caId) });
|
||||
queryClient.invalidateQueries({ queryKey: caKeys.getCaCert(caId) });
|
||||
queryClient.invalidateQueries({ queryKey: caKeys.getCaCerts(caId) });
|
||||
|
||||
@@ -182,4 +182,5 @@ export type TRenewCaResponse = {
|
||||
certificate: string;
|
||||
certificateChain: string;
|
||||
serialNumber: string;
|
||||
projectId: string;
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { caKeys } from "../ca/queries";
|
||||
import { workspaceKeys } from "../workspace";
|
||||
import { projectKeys } from "../projects";
|
||||
import { certTemplateKeys } from "./queries";
|
||||
import {
|
||||
TCertificateTemplate,
|
||||
@@ -29,7 +29,7 @@ export const useCreateCertTemplate = () => {
|
||||
},
|
||||
onSuccess: ({ caId }, { projectId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspaceCertificateTemplates(projectId)
|
||||
queryKey: projectKeys.getProjectCertificateTemplates(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: caKeys.getCaCertTemplates(caId) });
|
||||
}
|
||||
@@ -49,7 +49,7 @@ export const useUpdateCertTemplate = () => {
|
||||
},
|
||||
onSuccess: ({ caId }, { projectId, id }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspaceCertificateTemplates(projectId)
|
||||
queryKey: projectKeys.getProjectCertificateTemplates(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: certTemplateKeys.getCertTemplateById(id) });
|
||||
queryClient.invalidateQueries({ queryKey: caKeys.getCaCertTemplates(caId) });
|
||||
@@ -68,7 +68,7 @@ export const useDeleteCertTemplate = () => {
|
||||
},
|
||||
onSuccess: ({ caId }, { projectId, id }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspaceCertificateTemplates(projectId)
|
||||
queryKey: projectKeys.getProjectCertificateTemplates(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: certTemplateKeys.getCertTemplateById(id) });
|
||||
queryClient.invalidateQueries({ queryKey: caKeys.getCaCertTemplates(caId) });
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { pkiSubscriberKeys } from "../pkiSubscriber/queries";
|
||||
import { workspaceKeys } from "../workspace";
|
||||
import { projectKeys } from "../projects";
|
||||
import {
|
||||
TCertificate,
|
||||
TDeleteCertDTO,
|
||||
@@ -25,7 +25,7 @@ export const useDeleteCert = () => {
|
||||
},
|
||||
onSuccess: (_, { projectSlug }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.forWorkspaceCertificates(projectSlug)
|
||||
queryKey: projectKeys.forProjectCertificates(projectSlug)
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -47,7 +47,7 @@ export const useRevokeCert = () => {
|
||||
},
|
||||
onSuccess: (_, { projectSlug }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.forWorkspaceCertificates(projectSlug)
|
||||
queryKey: projectKeys.forProjectCertificates(projectSlug)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: pkiSubscriberKeys.allPkiSubscriberCertificates()
|
||||
@@ -68,7 +68,7 @@ export const useImportCertificate = () => {
|
||||
},
|
||||
onSuccess: (_, { projectSlug }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.forWorkspaceCertificates(projectSlug)
|
||||
queryKey: projectKeys.forProjectCertificates(projectSlug)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback } from "react";
|
||||
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
|
||||
import { useQuery, useQueryClient, UseQueryOptions } from "@tanstack/react-query";
|
||||
import { AxiosError } from "axios";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
@@ -10,13 +10,15 @@ import {
|
||||
DashboardProjectSecretsOverview,
|
||||
DashboardProjectSecretsOverviewResponse,
|
||||
DashboardSecretsOrderBy,
|
||||
DashboardSecretValue,
|
||||
TDashboardProjectSecretsQuickSearch,
|
||||
TDashboardProjectSecretsQuickSearchResponse,
|
||||
TGetAccessibleSecretsDTO,
|
||||
TGetDashboardProjectSecretsByKeys,
|
||||
TGetDashboardProjectSecretsDetailsDTO,
|
||||
TGetDashboardProjectSecretsOverviewDTO,
|
||||
TGetDashboardProjectSecretsQuickSearchDTO
|
||||
TGetDashboardProjectSecretsQuickSearchDTO,
|
||||
TGetSecretValueDTO
|
||||
} from "@app/hooks/api/dashboard/types";
|
||||
import { OrderByDirection } from "@app/hooks/api/generic/types";
|
||||
import { mergePersonalSecrets } from "@app/hooks/api/secrets/queries";
|
||||
@@ -73,6 +75,15 @@ export const dashboardKeys = {
|
||||
...dashboardKeys.all(),
|
||||
"accessible-secrets",
|
||||
{ projectId, secretPath, environment, filterByAction }
|
||||
] as const,
|
||||
getSecretValuesRoot: () => [...dashboardKeys.all(), "secrets-values"] as const,
|
||||
getSecretValue: ({ environment, secretPath, secretKey, isOverride }: TGetSecretValueDTO) =>
|
||||
[
|
||||
...dashboardKeys.getSecretValuesRoot(),
|
||||
environment,
|
||||
secretPath,
|
||||
secretKey,
|
||||
isOverride
|
||||
] as const
|
||||
};
|
||||
|
||||
@@ -174,6 +185,8 @@ export const useGetProjectSecretsOverview = (
|
||||
"queryKey" | "queryFn"
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useQuery({
|
||||
...options,
|
||||
// wait for all values to be available
|
||||
@@ -193,8 +206,8 @@ export const useGetProjectSecretsOverview = (
|
||||
includeSecretRotations,
|
||||
environments
|
||||
}),
|
||||
queryFn: () =>
|
||||
fetchProjectSecretsOverview({
|
||||
queryFn: async () => {
|
||||
const resp = fetchProjectSecretsOverview({
|
||||
secretPath,
|
||||
search,
|
||||
limit,
|
||||
@@ -208,7 +221,14 @@ export const useGetProjectSecretsOverview = (
|
||||
includeDynamicSecrets,
|
||||
includeSecretRotations,
|
||||
environments
|
||||
}),
|
||||
});
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: dashboardKeys.getSecretValuesRoot()
|
||||
});
|
||||
|
||||
return resp;
|
||||
},
|
||||
select: useCallback((data: Awaited<ReturnType<typeof fetchProjectSecretsOverview>>) => {
|
||||
const { secrets, secretRotations, ...select } = data;
|
||||
const uniqueSecrets = secrets ? unique(secrets, (i) => i.secretKey) : [];
|
||||
@@ -254,7 +274,6 @@ export const useGetProjectSecretsDetails = (
|
||||
search = "",
|
||||
includeSecrets,
|
||||
includeFolders,
|
||||
viewSecretValue,
|
||||
includeImports,
|
||||
includeDynamicSecrets,
|
||||
includeSecretRotations,
|
||||
@@ -270,6 +289,8 @@ export const useGetProjectSecretsDetails = (
|
||||
"queryKey" | "queryFn"
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useQuery({
|
||||
...options,
|
||||
// wait for all values to be available
|
||||
@@ -286,7 +307,6 @@ export const useGetProjectSecretsDetails = (
|
||||
limit,
|
||||
orderBy,
|
||||
orderDirection,
|
||||
viewSecretValue,
|
||||
offset,
|
||||
projectId,
|
||||
environment,
|
||||
@@ -297,14 +317,13 @@ export const useGetProjectSecretsDetails = (
|
||||
includeSecretRotations,
|
||||
tags
|
||||
}),
|
||||
queryFn: () =>
|
||||
fetchProjectSecretsDetails({
|
||||
queryFn: async () => {
|
||||
const resp = await fetchProjectSecretsDetails({
|
||||
secretPath,
|
||||
search,
|
||||
limit,
|
||||
orderBy,
|
||||
orderDirection,
|
||||
viewSecretValue,
|
||||
offset,
|
||||
projectId,
|
||||
environment,
|
||||
@@ -314,7 +333,14 @@ export const useGetProjectSecretsDetails = (
|
||||
includeDynamicSecrets,
|
||||
includeSecretRotations,
|
||||
tags
|
||||
}),
|
||||
});
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: dashboardKeys.getSecretValuesRoot()
|
||||
});
|
||||
|
||||
return resp;
|
||||
},
|
||||
select: useCallback(
|
||||
(data: Awaited<ReturnType<typeof fetchProjectSecretsDetails>>) => ({
|
||||
...data,
|
||||
@@ -471,3 +497,31 @@ export const useGetAccessibleSecrets = ({
|
||||
fetchAccessibleSecrets({ projectId, secretPath, environment, filterByAction, recursive })
|
||||
});
|
||||
};
|
||||
|
||||
export const fetchSecretValue = async (params: TGetSecretValueDTO) => {
|
||||
const { data } = await apiRequest.get<DashboardSecretValue>("/api/v1/dashboard/secret-value", {
|
||||
params
|
||||
});
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
export const useGetSecretValue = (
|
||||
params: TGetSecretValueDTO,
|
||||
options?: Omit<
|
||||
UseQueryOptions<
|
||||
DashboardSecretValue,
|
||||
unknown,
|
||||
DashboardSecretValue,
|
||||
ReturnType<typeof dashboardKeys.getSecretValue>
|
||||
>,
|
||||
"queryKey" | "queryFn"
|
||||
>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: dashboardKeys.getSecretValue(params),
|
||||
queryFn: async () => fetchSecretValue(params),
|
||||
staleTime: 1000 * 60,
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
@@ -112,7 +112,6 @@ export type TGetDashboardProjectSecretsDetailsDTO = Omit<
|
||||
TGetDashboardProjectSecretsOverviewDTO,
|
||||
"environments"
|
||||
> & {
|
||||
viewSecretValue: boolean;
|
||||
environment: string;
|
||||
includeImports?: boolean;
|
||||
tags: Record<string, boolean>;
|
||||
@@ -156,3 +155,21 @@ export type TGetAccessibleSecretsDTO = {
|
||||
| ProjectPermissionSecretActions.DescribeSecret
|
||||
| ProjectPermissionSecretActions.ReadValue;
|
||||
};
|
||||
|
||||
export type TGetSecretValueDTO = {
|
||||
projectId: string;
|
||||
secretKey: string;
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
isOverride?: boolean;
|
||||
};
|
||||
|
||||
export type DashboardSecretValue =
|
||||
| {
|
||||
value: string;
|
||||
valueOverride: undefined;
|
||||
}
|
||||
| {
|
||||
value: undefined;
|
||||
valueOverride: string;
|
||||
};
|
||||
|
||||
@@ -59,6 +59,11 @@ export enum DynamicSecretAwsIamAuth {
|
||||
IRSA = "irsa"
|
||||
}
|
||||
|
||||
export enum DynamicSecretAwsIamCredentialType {
|
||||
IamUser = "iam-user",
|
||||
TemporaryCredentials = "temporary-credentials"
|
||||
}
|
||||
|
||||
export type TDynamicSecretProvider =
|
||||
| {
|
||||
type: DynamicSecretProviders.SqlDatabase;
|
||||
@@ -97,6 +102,7 @@ export type TDynamicSecretProvider =
|
||||
inputs:
|
||||
| {
|
||||
method: DynamicSecretAwsIamAuth.AccessKey;
|
||||
credentialType: DynamicSecretAwsIamCredentialType;
|
||||
accessKey: string;
|
||||
secretAccessKey: string;
|
||||
region: string;
|
||||
@@ -107,6 +113,7 @@ export type TDynamicSecretProvider =
|
||||
}
|
||||
| {
|
||||
method: DynamicSecretAwsIamAuth.AssumeRole;
|
||||
credentialType: DynamicSecretAwsIamCredentialType;
|
||||
roleArn: string;
|
||||
region: string;
|
||||
awsPath?: string;
|
||||
@@ -116,6 +123,7 @@ export type TDynamicSecretProvider =
|
||||
}
|
||||
| {
|
||||
method: DynamicSecretAwsIamAuth.IRSA;
|
||||
credentialType: DynamicSecretAwsIamCredentialType;
|
||||
region: string;
|
||||
awsPath?: string;
|
||||
policyDocument?: string;
|
||||
|
||||
@@ -7,27 +7,27 @@ import { Commit, CommitHistoryItem, CommitWithChanges, RollbackPreview } from ".
|
||||
|
||||
export const commitKeys = {
|
||||
count: ({
|
||||
workspaceId,
|
||||
projectId,
|
||||
environment,
|
||||
directory
|
||||
}: {
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
environment: string;
|
||||
directory?: string;
|
||||
}) => [{ workspaceId, environment, directory }, "folder-commits-count"] as const,
|
||||
}) => [{ projectId, environment, directory }, "folder-commits-count"] as const,
|
||||
|
||||
history: ({
|
||||
workspaceId,
|
||||
projectId,
|
||||
environment,
|
||||
directory
|
||||
}: {
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
environment: string;
|
||||
directory?: string;
|
||||
}) => [{ workspaceId, environment, directory }, "folder-commits"] as const,
|
||||
}) => [{ projectId, environment, directory }, "folder-commits"] as const,
|
||||
|
||||
details: ({ workspaceId, commitId }: { workspaceId: string; commitId: string }) =>
|
||||
[{ workspaceId, commitId }, "commit-details"] as const,
|
||||
details: ({ projectId, commitId }: { projectId: string; commitId: string }) =>
|
||||
[{ projectId, commitId }, "commit-details"] as const,
|
||||
|
||||
rollbackPreview: ({
|
||||
folderId,
|
||||
@@ -45,11 +45,11 @@ export const commitKeys = {
|
||||
};
|
||||
|
||||
const fetchFolderCommitsCount = async ({
|
||||
workspaceId,
|
||||
projectId,
|
||||
environment,
|
||||
directory
|
||||
}: {
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
environment: string;
|
||||
directory?: string;
|
||||
}) => {
|
||||
@@ -59,7 +59,7 @@ const fetchFolderCommitsCount = async ({
|
||||
params: {
|
||||
environment,
|
||||
path: directory,
|
||||
projectId: workspaceId
|
||||
projectId
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -67,7 +67,7 @@ const fetchFolderCommitsCount = async ({
|
||||
};
|
||||
|
||||
const fetchFolderCommitHistory = async (
|
||||
workspaceId: string,
|
||||
projectId: string,
|
||||
environment: string,
|
||||
directory: string,
|
||||
offset: number = 0,
|
||||
@@ -87,7 +87,7 @@ const fetchFolderCommitHistory = async (
|
||||
params: {
|
||||
environment,
|
||||
path: directory,
|
||||
projectId: workspaceId,
|
||||
projectId,
|
||||
offset,
|
||||
limit,
|
||||
search,
|
||||
@@ -97,12 +97,12 @@ const fetchFolderCommitHistory = async (
|
||||
return res.data;
|
||||
};
|
||||
|
||||
export const fetchCommitDetails = async (workspaceId: string, commitId: string) => {
|
||||
export const fetchCommitDetails = async (projectId: string, commitId: string) => {
|
||||
const { data } = await apiRequest.get<CommitWithChanges>(
|
||||
`/api/v1/pit/commits/${commitId}/changes`,
|
||||
{
|
||||
params: {
|
||||
projectId: workspaceId
|
||||
projectId
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -113,7 +113,7 @@ export const fetchRollbackPreview = async (
|
||||
folderId: string,
|
||||
commitId: string,
|
||||
envSlug: string,
|
||||
workspaceId: string,
|
||||
projectId: string,
|
||||
deepRollback: boolean,
|
||||
secretPath: string
|
||||
): Promise<RollbackPreview[]> => {
|
||||
@@ -125,7 +125,7 @@ export const fetchRollbackPreview = async (
|
||||
environment: envSlug,
|
||||
deepRollback,
|
||||
secretPath,
|
||||
projectId: workspaceId
|
||||
projectId
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -135,7 +135,7 @@ export const fetchRollbackPreview = async (
|
||||
const fetchRollback = async (
|
||||
folderId: string,
|
||||
commitId: string,
|
||||
workspaceId: string,
|
||||
projectId: string,
|
||||
deepRollback: boolean,
|
||||
message?: string,
|
||||
envSlug?: string
|
||||
@@ -147,17 +147,17 @@ const fetchRollback = async (
|
||||
deepRollback,
|
||||
message,
|
||||
environment: envSlug,
|
||||
projectId: workspaceId
|
||||
projectId
|
||||
}
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
const fetchRevert = async (commitId: string, workspaceId: string) => {
|
||||
const fetchRevert = async (commitId: string, projectId: string) => {
|
||||
const { data } = await apiRequest.post<{ success: boolean; message: string }>(
|
||||
`/api/v1/pit/commits/${commitId}/revert`,
|
||||
{
|
||||
projectId: workspaceId
|
||||
projectId
|
||||
}
|
||||
);
|
||||
return data;
|
||||
@@ -180,9 +180,9 @@ export const useCommitRevert = ({
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [
|
||||
commitKeys.details({ workspaceId: projectId, commitId }),
|
||||
commitKeys.history({ workspaceId: projectId, environment, directory }),
|
||||
commitKeys.count({ workspaceId: projectId, environment, directory })
|
||||
commitKeys.details({ projectId, commitId }),
|
||||
commitKeys.history({ projectId, environment, directory }),
|
||||
commitKeys.count({ projectId, environment, directory })
|
||||
]
|
||||
});
|
||||
}
|
||||
@@ -190,7 +190,7 @@ export const useCommitRevert = ({
|
||||
};
|
||||
|
||||
export const useCommitRollback = ({
|
||||
workspaceId,
|
||||
projectId,
|
||||
commitId,
|
||||
folderId,
|
||||
deepRollback,
|
||||
@@ -198,7 +198,7 @@ export const useCommitRollback = ({
|
||||
directory,
|
||||
envSlug
|
||||
}: {
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
commitId: string;
|
||||
folderId: string;
|
||||
deepRollback: boolean;
|
||||
@@ -209,13 +209,13 @@ export const useCommitRollback = ({
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (message: string) =>
|
||||
fetchRollback(folderId, commitId, workspaceId, deepRollback, message, envSlug),
|
||||
fetchRollback(folderId, commitId, projectId, deepRollback, message, envSlug),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [
|
||||
commitKeys.details({ workspaceId, commitId }),
|
||||
commitKeys.history({ workspaceId, environment, directory }),
|
||||
commitKeys.count({ workspaceId, environment, directory })
|
||||
commitKeys.details({ projectId, commitId }),
|
||||
commitKeys.history({ projectId, environment, directory }),
|
||||
commitKeys.count({ projectId, environment, directory })
|
||||
]
|
||||
});
|
||||
}
|
||||
@@ -223,31 +223,31 @@ export const useCommitRollback = ({
|
||||
};
|
||||
|
||||
export const useGetFolderCommitsCount = ({
|
||||
workspaceId,
|
||||
projectId,
|
||||
environment,
|
||||
directory,
|
||||
isPaused
|
||||
}: {
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
environment: string;
|
||||
directory: string;
|
||||
isPaused?: boolean;
|
||||
}) =>
|
||||
useQuery({
|
||||
enabled: Boolean(workspaceId && environment) && !isPaused,
|
||||
queryKey: commitKeys.count({ workspaceId, environment, directory }),
|
||||
queryFn: () => fetchFolderCommitsCount({ workspaceId, environment, directory })
|
||||
enabled: Boolean(projectId && environment) && !isPaused,
|
||||
queryKey: commitKeys.count({ projectId, environment, directory }),
|
||||
queryFn: () => fetchFolderCommitsCount({ projectId, environment, directory })
|
||||
});
|
||||
|
||||
export const useGetFolderCommitHistory = ({
|
||||
workspaceId,
|
||||
projectId,
|
||||
environment,
|
||||
directory,
|
||||
limit = 20,
|
||||
search,
|
||||
sort = "desc"
|
||||
}: {
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
environment: string;
|
||||
directory: string;
|
||||
limit?: number;
|
||||
@@ -256,10 +256,10 @@ export const useGetFolderCommitHistory = ({
|
||||
}) => {
|
||||
return useInfiniteQuery({
|
||||
initialPageParam: 0,
|
||||
queryKey: [commitKeys.history({ workspaceId, environment, directory }), limit, search, sort],
|
||||
queryKey: [commitKeys.history({ projectId, environment, directory }), limit, search, sort],
|
||||
queryFn: ({ pageParam }) =>
|
||||
fetchFolderCommitHistory(workspaceId, environment, directory, pageParam, limit, search, sort),
|
||||
enabled: Boolean(workspaceId && environment),
|
||||
fetchFolderCommitHistory(projectId, environment, directory, pageParam, limit, search, sort),
|
||||
enabled: Boolean(projectId && environment),
|
||||
select: (data) => {
|
||||
return (data?.pages ?? [])
|
||||
?.map((page) => page.commits)
|
||||
@@ -280,11 +280,11 @@ export const useGetFolderCommitHistory = ({
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetCommitDetails = (workspaceId: string, commitId: string) => {
|
||||
export const useGetCommitDetails = (projectId: string, commitId: string) => {
|
||||
return useQuery({
|
||||
queryKey: commitKeys.details({ workspaceId, commitId }),
|
||||
queryFn: () => fetchCommitDetails(workspaceId, commitId),
|
||||
enabled: Boolean(workspaceId) && Boolean(commitId)
|
||||
queryKey: commitKeys.details({ projectId, commitId }),
|
||||
queryFn: () => fetchCommitDetails(projectId, commitId),
|
||||
enabled: Boolean(projectId) && Boolean(commitId)
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ export const useListProjectGroupUsers = ({
|
||||
});
|
||||
|
||||
const { data } = await apiRequest.get<{ users: TGroupUser[]; totalCount: number }>(
|
||||
`/api/v2/workspace/${projectId}/groups/${id}/users`,
|
||||
`/api/v1/projects/${projectId}/groups/${id}/users`,
|
||||
{
|
||||
params
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
AddIdentityTlsCertAuthDTO,
|
||||
AddIdentityTokenAuthDTO,
|
||||
AddIdentityUniversalAuthDTO,
|
||||
ClearIdentityLdapAuthLockoutsDTO,
|
||||
ClearIdentityUniversalAuthLockoutsDTO,
|
||||
ClientSecretData,
|
||||
CreateIdentityDTO,
|
||||
@@ -1432,7 +1433,11 @@ export const useAddIdentityLdapAuth = () => {
|
||||
accessTokenTTL,
|
||||
accessTokenMaxTTL,
|
||||
accessTokenNumUsesLimit,
|
||||
accessTokenTrustedIps
|
||||
accessTokenTrustedIps,
|
||||
lockoutEnabled,
|
||||
lockoutThreshold,
|
||||
lockoutDurationSeconds,
|
||||
lockoutCounterResetSeconds
|
||||
}) => {
|
||||
const { data } = await apiRequest.post<{ identityLdapAuth: IdentityLdapAuth }>(
|
||||
`/api/v1/auth/ldap-auth/identities/${identityId}`,
|
||||
@@ -1448,7 +1453,11 @@ export const useAddIdentityLdapAuth = () => {
|
||||
accessTokenTTL,
|
||||
accessTokenMaxTTL,
|
||||
accessTokenNumUsesLimit,
|
||||
accessTokenTrustedIps
|
||||
accessTokenTrustedIps,
|
||||
lockoutEnabled,
|
||||
lockoutThreshold,
|
||||
lockoutDurationSeconds,
|
||||
lockoutCounterResetSeconds
|
||||
}
|
||||
);
|
||||
return data.identityLdapAuth;
|
||||
@@ -1481,7 +1490,11 @@ export const useUpdateIdentityLdapAuth = () => {
|
||||
accessTokenTTL,
|
||||
accessTokenMaxTTL,
|
||||
accessTokenNumUsesLimit,
|
||||
accessTokenTrustedIps
|
||||
accessTokenTrustedIps,
|
||||
lockoutEnabled,
|
||||
lockoutThreshold,
|
||||
lockoutDurationSeconds,
|
||||
lockoutCounterResetSeconds
|
||||
}) => {
|
||||
const { data } = await apiRequest.patch<{ identityLdapAuth: IdentityLdapAuth }>(
|
||||
`/api/v1/auth/ldap-auth/identities/${identityId}`,
|
||||
@@ -1497,7 +1510,11 @@ export const useUpdateIdentityLdapAuth = () => {
|
||||
accessTokenTTL,
|
||||
accessTokenMaxTTL,
|
||||
accessTokenNumUsesLimit,
|
||||
accessTokenTrustedIps
|
||||
accessTokenTrustedIps,
|
||||
lockoutEnabled,
|
||||
lockoutThreshold,
|
||||
lockoutDurationSeconds,
|
||||
lockoutCounterResetSeconds
|
||||
}
|
||||
);
|
||||
return data.identityLdapAuth;
|
||||
@@ -1532,3 +1549,22 @@ export const useDeleteIdentityLdapAuth = () => {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useClearIdentityLdapAuthLockouts = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<number, object, ClearIdentityLdapAuthLockoutsDTO>({
|
||||
mutationFn: async ({ identityId }) => {
|
||||
const {
|
||||
data: { deleted }
|
||||
} = await apiRequest.post<{ deleted: number }>(
|
||||
`/api/v1/auth/ldap-auth/identities/${identityId}/clear-lockouts`
|
||||
);
|
||||
return deleted;
|
||||
},
|
||||
onSuccess: (_, { identityId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityLdapAuth(identityId)
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { OrderByDirection } from "../generic/types";
|
||||
import { OrgIdentityOrderBy } from "../organization/types";
|
||||
import { Project, ProjectUserMembershipTemporaryMode } from "../projects/types";
|
||||
import { TOrgRole } from "../roles/types";
|
||||
import { ProjectUserMembershipTemporaryMode, Workspace } from "../workspace/types";
|
||||
import { IdentityAuthMethod, IdentityJwtConfigurationType } from "./enums";
|
||||
|
||||
export type IdentityTrustedIp = {
|
||||
@@ -54,7 +54,7 @@ export type IdentityMembershipOrg = {
|
||||
export type IdentityMembership = {
|
||||
id: string;
|
||||
identity: Identity;
|
||||
project: Pick<Workspace, "id" | "name" | "type">;
|
||||
project: Pick<Project, "id" | "name" | "type">;
|
||||
roles: Array<
|
||||
{
|
||||
id: string;
|
||||
@@ -603,6 +603,11 @@ export type AddIdentityLdapAuthDTO = {
|
||||
accessTokenTrustedIps: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
|
||||
lockoutEnabled: boolean;
|
||||
lockoutThreshold: number;
|
||||
lockoutDurationSeconds: number;
|
||||
lockoutCounterResetSeconds: number;
|
||||
};
|
||||
|
||||
export type UpdateIdentityLdapAuthDTO = {
|
||||
@@ -625,6 +630,11 @@ export type UpdateIdentityLdapAuthDTO = {
|
||||
accessTokenTrustedIps?: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
|
||||
lockoutEnabled?: boolean;
|
||||
lockoutThreshold?: number;
|
||||
lockoutDurationSeconds?: number;
|
||||
lockoutCounterResetSeconds?: number;
|
||||
};
|
||||
|
||||
export type DeleteIdentityLdapAuthDTO = {
|
||||
@@ -650,6 +660,15 @@ export type IdentityLdapAuth = {
|
||||
accessTokenMaxTTL: number;
|
||||
accessTokenNumUsesLimit: number;
|
||||
accessTokenTrustedIps: IdentityTrustedIp[];
|
||||
|
||||
lockoutEnabled: boolean;
|
||||
lockoutThreshold: number;
|
||||
lockoutDurationSeconds: number;
|
||||
lockoutCounterResetSeconds: number;
|
||||
};
|
||||
|
||||
export type ClearIdentityLdapAuthLockoutsDTO = {
|
||||
identityId: string;
|
||||
};
|
||||
|
||||
export type AddIdentityTokenAuthDTO = {
|
||||
|
||||
@@ -20,7 +20,6 @@ export * from "./identityProjectAdditionalPrivilege";
|
||||
export * from "./incidentContacts";
|
||||
export * from "./integrationAuth";
|
||||
export * from "./integrations";
|
||||
export * from "./keys";
|
||||
export * from "./kms";
|
||||
export * from "./ldapConfig";
|
||||
export * from "./oidcConfig";
|
||||
@@ -29,6 +28,7 @@ export * from "./organization";
|
||||
export * from "./pkiAlerts";
|
||||
export * from "./pkiCollections";
|
||||
export * from "./pkiSubscriber";
|
||||
export * from "./projects";
|
||||
export * from "./projectUserAdditionalPrivilege";
|
||||
export * from "./rateLimit";
|
||||
export * from "./roles";
|
||||
@@ -54,4 +54,3 @@ export * from "./trustedIps";
|
||||
export * from "./users";
|
||||
export * from "./webhooks";
|
||||
export * from "./workflowIntegrations";
|
||||
export * from "./workspace";
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { apiRequest } from "@app/config/request";
|
||||
import { TReactQueryOptions } from "@app/types/reactQuery";
|
||||
|
||||
import { workspaceKeys } from "../workspace";
|
||||
import { projectKeys } from "../projects";
|
||||
import {
|
||||
App,
|
||||
BitbucketEnvironment,
|
||||
@@ -970,7 +970,7 @@ export const useAuthorizeIntegration = () => {
|
||||
},
|
||||
onSuccess: (res) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: { queryKey: workspaceKeys.getWorkspaceAuthorization(res.workspace) }
|
||||
queryKey: { queryKey: projectKeys.getProjectAuthorization(res.workspace) }
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1016,7 +1016,7 @@ export const useSaveIntegrationAccessToken = () => {
|
||||
},
|
||||
onSuccess: (res) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspaceAuthorization(res.workspace)
|
||||
queryKey: projectKeys.getProjectAuthorization(res.workspace)
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1035,10 +1035,10 @@ export const useDeleteIntegrationAuths = () => {
|
||||
),
|
||||
onSuccess: (_, { workspaceId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspaceAuthorization(workspaceId)
|
||||
queryKey: projectKeys.getProjectAuthorization(workspaceId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspaceIntegrations(workspaceId)
|
||||
queryKey: projectKeys.getProjectIntegrations(workspaceId)
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1052,10 +1052,10 @@ export const useDeleteIntegrationAuth = () => {
|
||||
mutationFn: ({ id }) => apiRequest.delete(`/api/v1/integration-auth/${id}`),
|
||||
onSuccess: (_, { workspaceId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspaceAuthorization(workspaceId)
|
||||
queryKey: projectKeys.getProjectAuthorization(workspaceId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspaceIntegrations(workspaceId)
|
||||
queryKey: projectKeys.getProjectIntegrations(workspaceId)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useMutation, useQuery, useQueryClient, UseQueryOptions } from "@tanstac
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { workspaceKeys } from "../workspace";
|
||||
import { projectKeys } from "../projects";
|
||||
import {
|
||||
IntegrationMetadataSyncMode,
|
||||
TCloudIntegration,
|
||||
@@ -121,7 +121,7 @@ export const useCreateIntegration = () => {
|
||||
},
|
||||
onSuccess: (res) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspaceIntegrations(res.workspace)
|
||||
queryKey: projectKeys.getProjectIntegrations(res.workspace)
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -141,10 +141,10 @@ export const useDeleteIntegration = () => {
|
||||
),
|
||||
onSuccess: (_, { workspaceId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspaceIntegrations(workspaceId)
|
||||
queryKey: projectKeys.getProjectIntegrations(workspaceId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspaceAuthorization(workspaceId)
|
||||
queryKey: projectKeys.getProjectAuthorization(workspaceId)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { useGetUserWsKey, useUploadWsKey } from "./queries";
|
||||
@@ -1,43 +0,0 @@
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { UploadWsKeyDTO, UserWsKeyPair } from "./types";
|
||||
|
||||
const encKeyKeys = {
|
||||
getUserWorkspaceKey: (workspaceID: string) => ["workspace-key-pair", { workspaceID }] as const
|
||||
};
|
||||
|
||||
export const fetchUserWsKey = async (projectId: string) => {
|
||||
const { data } = await apiRequest.get<UserWsKeyPair>(
|
||||
`/api/v2/workspace/${projectId}/encrypted-key`
|
||||
);
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
export const useGetUserWsKey = (workspaceID: string) =>
|
||||
useQuery({
|
||||
queryKey: encKeyKeys.getUserWorkspaceKey(workspaceID),
|
||||
queryFn: () => fetchUserWsKey(workspaceID),
|
||||
enabled: Boolean(workspaceID)
|
||||
});
|
||||
|
||||
// mutations
|
||||
export const uploadWsKey = async ({ workspaceId, userId, encryptedKey, nonce }: UploadWsKeyDTO) => {
|
||||
return apiRequest.post(`/api/v1/workspace/${workspaceId}/key`, {
|
||||
key: { userId, encryptedKey, nonce }
|
||||
});
|
||||
};
|
||||
|
||||
export const useUploadWsKey = () =>
|
||||
useMutation<object, object, UploadWsKeyDTO>({
|
||||
mutationFn: async ({ encryptedKey, nonce, userId, workspaceId }) => {
|
||||
return uploadWsKey({
|
||||
workspaceId,
|
||||
userId,
|
||||
encryptedKey,
|
||||
nonce
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,29 +0,0 @@
|
||||
export type UserWsKeyPair = {
|
||||
id: string;
|
||||
encryptedKey: string;
|
||||
nonce: string;
|
||||
sender: Sender;
|
||||
receiver: string;
|
||||
workspace: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
__v: number;
|
||||
};
|
||||
|
||||
export type Sender = {
|
||||
id: string;
|
||||
email: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
__v: number;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
publicKey: string;
|
||||
};
|
||||
|
||||
export type UploadWsKeyDTO = {
|
||||
userId: string;
|
||||
encryptedKey: string;
|
||||
nonce: string;
|
||||
workspaceId: string;
|
||||
};
|
||||
@@ -75,7 +75,7 @@ export const useUpdateProjectKms = (projectId: string) => {
|
||||
mutationFn: async (
|
||||
updatedData: { type: KmsType.Internal } | { type: KmsType.External; kmsId: string }
|
||||
) => {
|
||||
const { data } = await apiRequest.patch(`/api/v1/workspace/${projectId}/kms`, {
|
||||
const { data } = await apiRequest.patch(`/api/v1/projects/${projectId}/kms`, {
|
||||
kms: updatedData
|
||||
});
|
||||
|
||||
@@ -91,7 +91,7 @@ export const useLoadProjectKmsBackup = (projectId: string) => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async (backup: string) => {
|
||||
const { data } = await apiRequest.post(`/api/v1/workspace/${projectId}/kms/backup`, {
|
||||
const { data } = await apiRequest.post(`/api/v1/projects/${projectId}/kms/backup`, {
|
||||
backup
|
||||
});
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ export const useGetActiveProjectKms = (projectId: string) => {
|
||||
name: string;
|
||||
isExternal: string;
|
||||
};
|
||||
}>(`/api/v1/workspace/${projectId}/kms`);
|
||||
}>(`/api/v1/projects/${projectId}/kms`);
|
||||
return secretManagerKmsKey;
|
||||
}
|
||||
});
|
||||
@@ -58,7 +58,7 @@ export const useGetActiveProjectKms = (projectId: string) => {
|
||||
export const fetchProjectKmsBackup = async (projectId: string) => {
|
||||
const { data } = await apiRequest.get<{
|
||||
secretManager: string;
|
||||
}>(`/api/v1/workspace/${projectId}/kms/backup`);
|
||||
}>(`/api/v1/projects/${projectId}/kms/backup`);
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { workspaceKeys } from "../workspace";
|
||||
import { projectKeys } from "../projects";
|
||||
|
||||
export const useImportEnvKey = () => {
|
||||
const queryClient = useQueryClient();
|
||||
@@ -34,7 +34,7 @@ export const useImportEnvKey = () => {
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getAllUserWorkspace()
|
||||
queryKey: projectKeys.getAllUserProjects()
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { workspaceKeys } from "../workspace";
|
||||
import { projectKeys } from "../projects";
|
||||
import { pkiAlertKeys } from "./queries";
|
||||
import { TCreatePkiAlertDTO, TDeletePkiAlertDTO, TPkiAlert, TUpdatePkiAlertDTO } from "./types";
|
||||
|
||||
@@ -14,7 +14,7 @@ export const useCreatePkiAlert = () => {
|
||||
return alert;
|
||||
},
|
||||
onSuccess: (_, { projectId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: workspaceKeys.getWorkspacePkiAlerts(projectId) });
|
||||
queryClient.invalidateQueries({ queryKey: projectKeys.getProjectPkiAlerts(projectId) });
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -30,7 +30,7 @@ export const useUpdatePkiAlert = () => {
|
||||
return alert;
|
||||
},
|
||||
onSuccess: (_, { projectId, alertId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: workspaceKeys.getWorkspacePkiAlerts(projectId) });
|
||||
queryClient.invalidateQueries({ queryKey: projectKeys.getProjectPkiAlerts(projectId) });
|
||||
queryClient.invalidateQueries({ queryKey: pkiAlertKeys.getPkiAlertById(alertId) });
|
||||
}
|
||||
});
|
||||
@@ -44,7 +44,7 @@ export const useDeletePkiAlert = () => {
|
||||
return alert;
|
||||
},
|
||||
onSuccess: (_, { projectId, alertId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: workspaceKeys.getWorkspacePkiAlerts(projectId) });
|
||||
queryClient.invalidateQueries({ queryKey: projectKeys.getProjectPkiAlerts(projectId) });
|
||||
queryClient.invalidateQueries({ queryKey: pkiAlertKeys.getPkiAlertById(alertId) });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { workspaceKeys } from "../workspace";
|
||||
import { projectKeys } from "../projects";
|
||||
import { pkiCollectionKeys } from "./queries";
|
||||
import {
|
||||
TAddItemToPkiCollectionDTO,
|
||||
@@ -26,7 +26,7 @@ export const useCreatePkiCollection = () => {
|
||||
},
|
||||
onSuccess: (_, { projectId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspacePkiCollections(projectId)
|
||||
queryKey: projectKeys.getProjectPkiCollections(projectId)
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -44,7 +44,7 @@ export const useUpdatePkiCollection = () => {
|
||||
},
|
||||
onSuccess: (_, { projectId, collectionId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspacePkiCollections(projectId)
|
||||
queryKey: projectKeys.getProjectPkiCollections(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: pkiCollectionKeys.getPkiCollectionById(collectionId)
|
||||
@@ -64,7 +64,7 @@ export const useDeletePkiCollection = () => {
|
||||
},
|
||||
onSuccess: (_, { projectId, collectionId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspacePkiCollections(projectId)
|
||||
queryKey: projectKeys.getProjectPkiCollections(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: pkiCollectionKeys.getPkiCollectionById(collectionId)
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { TCreateCertificateResponse } from "../ca/types";
|
||||
import { workspaceKeys } from "../workspace/query-keys";
|
||||
import { projectKeys } from "../projects/query-keys";
|
||||
import { pkiSubscriberKeys } from "./queries";
|
||||
import {
|
||||
TCreatePkiSubscriberDTO,
|
||||
@@ -22,7 +22,7 @@ export const useCreatePkiSubscriber = () => {
|
||||
},
|
||||
onSuccess: ({ projectId, name }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspacePkiSubscribers(projectId)
|
||||
queryKey: projectKeys.getProjectPkiSubscribers(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: pkiSubscriberKeys.getPkiSubscriber({
|
||||
@@ -46,7 +46,7 @@ export const useUpdatePkiSubscriber = () => {
|
||||
},
|
||||
onSuccess: ({ projectId, name }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspacePkiSubscribers(projectId)
|
||||
queryKey: projectKeys.getProjectPkiSubscribers(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: pkiSubscriberKeys.getPkiSubscriber({
|
||||
@@ -74,7 +74,7 @@ export const useDeletePkiSubscriber = () => {
|
||||
},
|
||||
onSuccess: ({ name, projectId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspacePkiSubscribers(projectId)
|
||||
queryKey: projectKeys.getProjectPkiSubscribers(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: pkiSubscriberKeys.getPkiSubscriber({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { TProjectRole } from "@app/hooks/api/roles/types";
|
||||
|
||||
import { ProjectType } from "../workspace/types";
|
||||
import { ProjectType } from "../projects/types";
|
||||
|
||||
export type TProjectTemplate = {
|
||||
id: string;
|
||||
|
||||
@@ -17,15 +17,14 @@ export {
|
||||
useDeleteWsEnvironment,
|
||||
useGetProjectSshConfig,
|
||||
useGetUpgradeProjectStatus,
|
||||
useGetUserProjects,
|
||||
useGetUserWorkspaceMemberships,
|
||||
useGetUserWorkspaces,
|
||||
useGetWorkspaceAuthorizations,
|
||||
useGetWorkspaceById,
|
||||
useGetWorkspaceIdentityMembershipDetails,
|
||||
useGetWorkspaceIdentityMemberships,
|
||||
useGetWorkspaceIndexStatus,
|
||||
useGetWorkspaceIntegrations,
|
||||
useGetWorkspaceSecrets,
|
||||
useGetWorkspaceUserDetails,
|
||||
useGetWorkspaceUsers,
|
||||
useGetWorkspaceWorkflowIntegrationConfig,
|
||||
@@ -41,13 +40,11 @@ export {
|
||||
useListWorkspaceSshCertificateTemplates,
|
||||
useListWorkspaceSshHostGroups,
|
||||
useListWorkspaceSshHosts,
|
||||
useNameWorkspaceSecrets,
|
||||
useSearchProjects,
|
||||
useToggleAutoCapitalization,
|
||||
useUpdateIdentityWorkspaceRole,
|
||||
useUpdateProject,
|
||||
useUpdateUserWorkspaceRole,
|
||||
useUpdateWsEnvironment,
|
||||
useUpgradeProject
|
||||
} from "./queries";
|
||||
export { workspaceKeys } from "./query-keys";
|
||||
export { projectKeys } from "./query-keys";
|
||||
@@ -3,7 +3,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { userKeys } from "../users/query-keys";
|
||||
import { workspaceKeys } from "./query-keys";
|
||||
import { projectKeys } from "./query-keys";
|
||||
import {
|
||||
TProjectSshConfig,
|
||||
TUpdateProjectSshConfigDTO,
|
||||
@@ -24,7 +24,7 @@ export const useAddGroupToWorkspace = () => {
|
||||
}) => {
|
||||
const {
|
||||
data: { groupMembership }
|
||||
} = await apiRequest.post(`/api/v2/workspace/${projectId}/groups/${groupId}`, {
|
||||
} = await apiRequest.post(`/api/v1/projects/${projectId}/groups/${groupId}`, {
|
||||
role
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ export const useAddGroupToWorkspace = () => {
|
||||
},
|
||||
onSuccess: (_, { projectId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspaceGroupMemberships(projectId)
|
||||
queryKey: projectKeys.getProjectGroupMemberships(projectId)
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -44,7 +44,7 @@ export const useUpdateGroupWorkspaceRole = () => {
|
||||
mutationFn: async ({ groupId, projectId, roles }: TUpdateWorkspaceGroupRoleDTO) => {
|
||||
const {
|
||||
data: { groupMembership }
|
||||
} = await apiRequest.patch(`/api/v2/workspace/${projectId}/groups/${groupId}`, {
|
||||
} = await apiRequest.patch(`/api/v1/projects/${projectId}/groups/${groupId}`, {
|
||||
roles
|
||||
});
|
||||
|
||||
@@ -52,10 +52,10 @@ export const useUpdateGroupWorkspaceRole = () => {
|
||||
},
|
||||
onSuccess: (_, { projectId, groupId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspaceGroupMemberships(projectId)
|
||||
queryKey: projectKeys.getProjectGroupMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspaceGroupMembershipDetails(projectId, groupId)
|
||||
queryKey: projectKeys.getProjectGroupMembershipDetails(projectId, groupId)
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -74,12 +74,12 @@ export const useDeleteGroupFromWorkspace = () => {
|
||||
}) => {
|
||||
const {
|
||||
data: { groupMembership }
|
||||
} = await apiRequest.delete(`/api/v2/workspace/${projectId}/groups/${groupId}`);
|
||||
} = await apiRequest.delete(`/api/v1/projects/${projectId}/groups/${groupId}`);
|
||||
return groupMembership;
|
||||
},
|
||||
onSuccess: (_, { projectId, username }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getWorkspaceGroupMemberships(projectId)
|
||||
queryKey: projectKeys.getProjectGroupMemberships(projectId)
|
||||
});
|
||||
|
||||
if (username) {
|
||||
@@ -91,25 +91,25 @@ export const useDeleteGroupFromWorkspace = () => {
|
||||
|
||||
export const useLeaveProject = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<object, object, { workspaceId: string }>({
|
||||
mutationFn: ({ workspaceId }) => {
|
||||
return apiRequest.delete(`/api/v1/workspace/${workspaceId}/leave`);
|
||||
return useMutation<object, object, { projectId: string }>({
|
||||
mutationFn: ({ projectId }) => {
|
||||
return apiRequest.delete(`/api/v1/projects/${projectId}/leave`);
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: workspaceKeys.getAllUserWorkspace() });
|
||||
queryClient.invalidateQueries({ queryKey: projectKeys.getAllUserProjects() });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useMigrateProjectToV3 = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<object, object, { workspaceId: string }>({
|
||||
mutationFn: ({ workspaceId }) => {
|
||||
return apiRequest.post(`/api/v1/workspace/${workspaceId}/migrate-v3`);
|
||||
return useMutation<object, object, { projectId: string }>({
|
||||
mutationFn: ({ projectId }) => {
|
||||
return apiRequest.post(`/api/v1/projects/${projectId}/migrate-v3`);
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getAllUserWorkspace()
|
||||
queryKey: projectKeys.getAllUserProjects()
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -118,7 +118,7 @@ export const useMigrateProjectToV3 = () => {
|
||||
export const useRequestProjectAccess = () => {
|
||||
return useMutation<object, object, { projectId: string; comment: string }>({
|
||||
mutationFn: ({ projectId, comment }) => {
|
||||
return apiRequest.post(`/api/v1/workspace/${projectId}/project-access`, {
|
||||
return apiRequest.post(`/api/v1/projects/${projectId}/project-access`, {
|
||||
comment
|
||||
});
|
||||
}
|
||||
@@ -129,14 +129,14 @@ export const useUpdateProjectSshConfig = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<TProjectSshConfig, object, TUpdateProjectSshConfigDTO>({
|
||||
mutationFn: ({ projectId, defaultUserSshCaId, defaultHostSshCaId }) => {
|
||||
return apiRequest.patch(`/api/v1/workspace/${projectId}/ssh-config`, {
|
||||
return apiRequest.patch(`/api/v1/projects/${projectId}/ssh-config`, {
|
||||
defaultUserSshCaId,
|
||||
defaultHostSshCaId
|
||||
});
|
||||
},
|
||||
onSuccess: (_, { projectId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: workspaceKeys.getProjectSshConfig(projectId)
|
||||
queryKey: projectKeys.getProjectSshConfig(projectId)
|
||||
});
|
||||
}
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
77
frontend/src/hooks/api/projects/query-keys.tsx
Normal file
77
frontend/src/hooks/api/projects/query-keys.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import type { CaStatus } from "../ca";
|
||||
import { WorkflowIntegrationPlatform } from "../workflowIntegrations/types";
|
||||
import { TListProjectIdentitiesDTO, TSearchProjectsDTO } from "./types";
|
||||
|
||||
export const projectKeys = {
|
||||
getProjectById: (projectId: string) => ["projects", { projectId }] as const,
|
||||
getProjectSecrets: (projectId: string) => [{ projectId }, "project-secrets"] as const,
|
||||
getProjectIndexStatus: (projectId: string) => [{ projectId }, "project-index-status"] as const,
|
||||
getProjectUpgradeStatus: (projectId: string) => [{ projectId }, "project-upgrade-status"],
|
||||
getProjectMemberships: (orgId: string) => [{ orgId }, "project-memberships"],
|
||||
getProjectAuthorization: (projectId: string) => [{ projectId }, "project-authorizations"],
|
||||
getProjectIntegrations: (projectId: string) => [{ projectId }, "project-integrations"],
|
||||
getAllUserProjects: () => ["projects"] as const,
|
||||
getProjectAuditLogs: (projectId: string) => [{ projectId }, "project-audit-logs"] as const,
|
||||
getProjectUsers: (
|
||||
projectId: string,
|
||||
includeGroupMembers: boolean = false,
|
||||
roles: string[] = []
|
||||
) => [{ projectId, includeGroupMembers, roles }, "project-users"] as const,
|
||||
getProjectUserDetails: (projectId: string, membershipId: string) =>
|
||||
[{ projectId, membershipId }, "project-user-details"] as const,
|
||||
getProjectIdentityMemberships: (projectId: string) =>
|
||||
[{ projectId }, "project-identity-memberships"] as const,
|
||||
getProjectIdentityMembershipDetails: (projectId: string, identityId: string) =>
|
||||
[{ projectId, identityId }, "project-identity-membership-details"] as const,
|
||||
// allows invalidation using above key without knowing params
|
||||
getProjectIdentityMembershipsWithParams: ({ projectId, ...params }: TListProjectIdentitiesDTO) =>
|
||||
[...projectKeys.getProjectIdentityMemberships(projectId), params] as const,
|
||||
searchProject: (dto: TSearchProjectsDTO) => ["search-projects", dto] as const,
|
||||
getProjectGroupMemberships: (projectId: string) => [{ projectId }, "project-groups"] as const,
|
||||
getProjectGroupMembershipDetails: (projectId: string, groupId: string) =>
|
||||
[{ projectId, groupId }, "project-group-membership-details"] as const,
|
||||
getProjectCas: ({ projectId }: { projectId: string }) => [{ projectId }, "project-cas"] as const,
|
||||
specificProjectCas: ({ projectId, status }: { projectId: string; status?: CaStatus }) =>
|
||||
[...projectKeys.getProjectCas({ projectId }), { status }] as const,
|
||||
allProjectCertificates: () => ["project-certificates"] as const,
|
||||
forProjectCertificates: (projectId: string) =>
|
||||
[...projectKeys.allProjectCertificates(), projectId] as const,
|
||||
specificProjectCertificates: ({
|
||||
projectId,
|
||||
offset,
|
||||
limit
|
||||
}: {
|
||||
projectId: string;
|
||||
offset: number;
|
||||
limit: number;
|
||||
}) => [...projectKeys.forProjectCertificates(projectId), { offset, limit }] as const,
|
||||
getProjectPkiAlerts: (projectId: string) => [{ projectId }, "project-pki-alerts"] as const,
|
||||
getProjectPkiSubscribers: (projectId: string) =>
|
||||
[{ projectId }, "project-pki-subscribers"] as const,
|
||||
getProjectPkiCollections: (projectId: string) =>
|
||||
[{ projectId }, "project-pki-collections"] as const,
|
||||
getProjectCertificateTemplates: (projectId: string) =>
|
||||
[{ projectId }, "project-certificate-templates"] as const,
|
||||
getProjectWorkflowIntegrationConfig: (
|
||||
projectId: string,
|
||||
integration: WorkflowIntegrationPlatform
|
||||
) => [{ projectId, integration }, "project-workflow-integration-config"] as const,
|
||||
getProjectSshCas: (projectId: string) => [{ projectId }, "project-ssh-cas"] as const,
|
||||
allProjectSshCertificates: (projectId: string) =>
|
||||
[{ projectId }, "project-ssh-certificates"] as const,
|
||||
getProjectSshHosts: (projectId: string) => [{ projectId }, "project-ssh-hosts"] as const,
|
||||
getProjectSshHostGroups: (projectId: string) =>
|
||||
[{ projectId }, "project-ssh-host-groups"] as const,
|
||||
specificProjectSshCertificates: ({
|
||||
offset,
|
||||
limit,
|
||||
projectId
|
||||
}: {
|
||||
offset: number;
|
||||
limit: number;
|
||||
projectId: string;
|
||||
}) => [...projectKeys.allProjectSshCertificates(projectId), { offset, limit }] as const,
|
||||
getProjectSshCertificateTemplates: (projectId: string) =>
|
||||
[{ projectId }, "project-ssh-certificate-templates"] as const,
|
||||
getProjectSshConfig: (projectId: string) => [{ projectId }, "project-ssh-config"] as const
|
||||
};
|
||||
@@ -20,7 +20,7 @@ export enum ProjectUserMembershipTemporaryMode {
|
||||
Relative = "relative"
|
||||
}
|
||||
|
||||
export type Workspace = {
|
||||
export type Project = {
|
||||
__v: number;
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -31,7 +31,7 @@ export type Workspace = {
|
||||
upgradeStatus: string | null;
|
||||
updatedAt: string;
|
||||
autoCapitalization: boolean;
|
||||
environments: WorkspaceEnv[];
|
||||
environments: ProjectEnv[];
|
||||
pitVersionLimit: number;
|
||||
auditLogsRetentionDays: number;
|
||||
slug: string;
|
||||
@@ -43,16 +43,16 @@ export type Workspace = {
|
||||
secretDetectionIgnoreValues: string[];
|
||||
};
|
||||
|
||||
export type WorkspaceEnv = {
|
||||
export type ProjectEnv = {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
};
|
||||
|
||||
export type WorkspaceTag = { id: string; name: string; slug: string };
|
||||
export type ProjectTag = { id: string; name: string; slug: string };
|
||||
|
||||
export type NameWorkspaceSecretsDTO = {
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
secretsToUpdate: {
|
||||
secretName: string;
|
||||
secretId: string;
|
||||
@@ -76,30 +76,33 @@ export type CreateWorkspaceDTO = {
|
||||
};
|
||||
|
||||
export type UpdateProjectDTO = {
|
||||
projectID: string;
|
||||
projectId: string;
|
||||
newProjectName?: string;
|
||||
newProjectDescription?: string;
|
||||
newSlug?: string;
|
||||
secretSharing?: boolean;
|
||||
showSnapshotsLegacy?: boolean;
|
||||
secretDetectionIgnoreValues?: string[];
|
||||
pitVersionLimit?: number;
|
||||
autoCapitalization?: boolean;
|
||||
hasDeleteProtection?: boolean;
|
||||
};
|
||||
|
||||
export type UpdatePitVersionLimitDTO = { projectSlug: string; pitVersionLimit: number };
|
||||
export type UpdateAuditLogsRetentionDTO = { projectSlug: string; auditLogsRetentionDays: number };
|
||||
export type ToggleAutoCapitalizationDTO = { workspaceID: string; state: boolean };
|
||||
export type ToggleDeleteProjectProtectionDTO = { workspaceID: string; state: boolean };
|
||||
export type ToggleAutoCapitalizationDTO = { projectID: string; state: boolean };
|
||||
export type ToggleDeleteProjectProtectionDTO = { projectID: string; state: boolean };
|
||||
|
||||
export type DeleteWorkspaceDTO = { workspaceID: string };
|
||||
export type DeleteWorkspaceDTO = { projectID: string };
|
||||
|
||||
export type CreateEnvironmentDTO = {
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
};
|
||||
|
||||
export type ReorderEnvironmentsDTO = {
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
environmentSlug: string;
|
||||
environmentName: string;
|
||||
otherEnvironmentSlug: string;
|
||||
@@ -107,18 +110,18 @@ export type ReorderEnvironmentsDTO = {
|
||||
};
|
||||
|
||||
export type UpdateEnvironmentDTO = {
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
id: string;
|
||||
name?: string;
|
||||
slug?: string;
|
||||
position?: number;
|
||||
};
|
||||
|
||||
export type DeleteEnvironmentDTO = { workspaceId: string; id: string };
|
||||
export type DeleteEnvironmentDTO = { projectId: string; id: string };
|
||||
|
||||
export type TUpdateWorkspaceUserRoleDTO = {
|
||||
membershipId: string;
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
roles: (
|
||||
| {
|
||||
role: string;
|
||||
@@ -136,7 +139,7 @@ export type TUpdateWorkspaceUserRoleDTO = {
|
||||
|
||||
export type TUpdateWorkspaceIdentityRoleDTO = {
|
||||
identityId: string;
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
roles: (
|
||||
| {
|
||||
role: string;
|
||||
@@ -171,7 +174,7 @@ export type TUpdateWorkspaceGroupRoleDTO = {
|
||||
};
|
||||
|
||||
export type TListProjectIdentitiesDTO = {
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
orderBy?: ProjectIdentityOrderBy;
|
||||
@@ -186,6 +189,7 @@ export type TSearchProjectsDTO = {
|
||||
name?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
projectIds?: string[];
|
||||
type?: ProjectType;
|
||||
options?: { enabled?: boolean };
|
||||
orderBy?: ProjectIdentityOrderBy;
|
||||
3
frontend/src/hooks/api/relays/index.tsx
Normal file
3
frontend/src/hooks/api/relays/index.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./mutations";
|
||||
export * from "./queries";
|
||||
export * from "./types";
|
||||
17
frontend/src/hooks/api/relays/mutations.tsx
Normal file
17
frontend/src/hooks/api/relays/mutations.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { relayQueryKeys } from "./queries";
|
||||
|
||||
export const useDeleteRelayById = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => {
|
||||
return apiRequest.delete(`/api/v1/relays/${id}`);
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: relayQueryKeys.list() });
|
||||
}
|
||||
});
|
||||
};
|
||||
21
frontend/src/hooks/api/relays/queries.tsx
Normal file
21
frontend/src/hooks/api/relays/queries.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { TRelay } from "./types";
|
||||
|
||||
export const relayQueryKeys = {
|
||||
list: () => ["relays"] as const
|
||||
};
|
||||
|
||||
const fetchRelays = async (): Promise<TRelay[]> => {
|
||||
const { data } = await apiRequest.get<TRelay[]>("/api/v1/relays");
|
||||
return data;
|
||||
};
|
||||
|
||||
export const useGetRelays = () => {
|
||||
return useQuery({
|
||||
queryKey: relayQueryKeys.list(),
|
||||
queryFn: fetchRelays
|
||||
});
|
||||
};
|
||||
13
frontend/src/hooks/api/relays/types.ts
Normal file
13
frontend/src/hooks/api/relays/types.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export type TRelay = {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
orgId: string | null;
|
||||
identityId: string | null;
|
||||
name: string;
|
||||
host: string;
|
||||
};
|
||||
|
||||
export type TDeleteRelayDTO = {
|
||||
id: string;
|
||||
};
|
||||
@@ -22,7 +22,7 @@ export const useCreateProjectRole = () => {
|
||||
mutationFn: async ({ projectId, ...dto }: TCreateProjectRoleDTO) => {
|
||||
const {
|
||||
data: { role }
|
||||
} = await apiRequest.post(`/api/v2/workspace/${projectId}/roles`, dto);
|
||||
} = await apiRequest.post(`/api/v1/projects/${projectId}/roles`, dto);
|
||||
return role;
|
||||
},
|
||||
onSuccess: (_, { projectId }) => {
|
||||
@@ -38,7 +38,7 @@ export const useUpdateProjectRole = () => {
|
||||
mutationFn: async ({ id, projectId, ...dto }: TUpdateProjectRoleDTO) => {
|
||||
const {
|
||||
data: { role }
|
||||
} = await apiRequest.patch(`/api/v2/workspace/${projectId}/roles/${id}`, dto);
|
||||
} = await apiRequest.patch(`/api/v1/projects/${projectId}/roles/${id}`, dto);
|
||||
return role;
|
||||
},
|
||||
onSuccess: (_, { projectId, slug }) => {
|
||||
@@ -58,7 +58,7 @@ export const useDeleteProjectRole = () => {
|
||||
mutationFn: async ({ projectId, id }: TDeleteProjectRoleDTO) => {
|
||||
const {
|
||||
data: { role }
|
||||
} = await apiRequest.delete(`/api/v2/workspace/${projectId}/roles/${id}`);
|
||||
} = await apiRequest.delete(`/api/v1/projects/${projectId}/roles/${id}`);
|
||||
return role;
|
||||
},
|
||||
onSuccess: (_, { projectId }) => {
|
||||
|
||||
@@ -46,13 +46,13 @@ export const roleQueryKeys = {
|
||||
getOrgRole: (orgId: string, roleId: string) => [{ orgId, roleId }, "org-role"] as const,
|
||||
getUserOrgPermissions: ({ orgId }: TGetUserOrgPermissionsDTO) =>
|
||||
["user-permissions", { orgId }] as const,
|
||||
getUserProjectPermissions: ({ workspaceId }: TGetUserProjectPermissionDTO) =>
|
||||
["user-project-permissions", { workspaceId }] as const
|
||||
getUserProjectPermissions: ({ projectId }: TGetUserProjectPermissionDTO) =>
|
||||
["user-project-permissions", { projectId }] as const
|
||||
};
|
||||
|
||||
export const getProjectRoles = async (projectId: string) => {
|
||||
const { data } = await apiRequest.get<{ roles: Array<Omit<TProjectRole, "permissions">> }>(
|
||||
`/api/v2/workspace/${projectId}/roles`
|
||||
`/api/v1/projects/${projectId}/roles`
|
||||
);
|
||||
return data.roles;
|
||||
};
|
||||
@@ -69,7 +69,7 @@ export const useGetProjectRoleBySlug = (projectId: string, roleSlug: string) =>
|
||||
queryKey: roleQueryKeys.getProjectRoleBySlug(projectId, roleSlug),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<{ role: TProjectRole }>(
|
||||
`/api/v2/workspace/${projectId}/roles/slug/${roleSlug}`
|
||||
`/api/v1/projects/${projectId}/roles/slug/${roleSlug}`
|
||||
);
|
||||
return data.role;
|
||||
},
|
||||
@@ -131,9 +131,7 @@ export const useGetUserOrgPermissions = ({ orgId }: TGetUserOrgPermissionsDTO) =
|
||||
}
|
||||
});
|
||||
|
||||
export const fetchUserProjectPermissions = async ({
|
||||
workspaceId
|
||||
}: TGetUserProjectPermissionDTO) => {
|
||||
export const fetchUserProjectPermissions = async ({ projectId }: TGetUserProjectPermissionDTO) => {
|
||||
const { data } = await apiRequest.get<{
|
||||
data: {
|
||||
permissions: PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[];
|
||||
@@ -145,16 +143,16 @@ export const fetchUserProjectPermissions = async ({
|
||||
actorName: string;
|
||||
};
|
||||
};
|
||||
}>(`/api/v1/workspace/${workspaceId}/permissions`, {});
|
||||
}>(`/api/v1/projects/${projectId}/permissions`, {});
|
||||
|
||||
return data.data;
|
||||
};
|
||||
|
||||
export const useGetUserProjectPermissions = ({ workspaceId }: TGetUserProjectPermissionDTO) =>
|
||||
export const useGetUserProjectPermissions = ({ projectId }: TGetUserProjectPermissionDTO) =>
|
||||
useQuery({
|
||||
queryKey: roleQueryKeys.getUserProjectPermissions({ workspaceId }),
|
||||
queryFn: () => fetchUserProjectPermissions({ workspaceId }),
|
||||
enabled: Boolean(workspaceId),
|
||||
queryKey: roleQueryKeys.getUserProjectPermissions({ projectId }),
|
||||
queryFn: () => fetchUserProjectPermissions({ projectId }),
|
||||
enabled: Boolean(projectId),
|
||||
select: (data) => {
|
||||
const rule = unpackRules<RawRuleOf<MongoAbility<ProjectPermissionSet>>>(data.permissions);
|
||||
const negatedRules = groupBy(
|
||||
|
||||
@@ -9,7 +9,7 @@ export enum ProjectMembershipRole {
|
||||
}
|
||||
|
||||
export type TGetProjectRolesDTO = {
|
||||
workspaceId?: string;
|
||||
projectId?: string;
|
||||
};
|
||||
|
||||
export type TProjectRole = {
|
||||
@@ -52,7 +52,7 @@ export type TGetUserOrgPermissionsDTO = {
|
||||
};
|
||||
|
||||
export type TGetUserProjectPermissionDTO = {
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
};
|
||||
|
||||
export type TCreateOrgRoleDTO = {
|
||||
|
||||
@@ -11,7 +11,7 @@ export const useCreateSecretApprovalPolicy = () => {
|
||||
return useMutation<object, object, TCreateSecretPolicyDTO>({
|
||||
mutationFn: async ({
|
||||
environments,
|
||||
workspaceId,
|
||||
projectId,
|
||||
approvals,
|
||||
approvers,
|
||||
bypassers,
|
||||
@@ -20,9 +20,9 @@ export const useCreateSecretApprovalPolicy = () => {
|
||||
enforcementLevel,
|
||||
allowedSelfApprovals
|
||||
}) => {
|
||||
const { data } = await apiRequest.post("/api/v1/secret-approvals", {
|
||||
const { data } = await apiRequest.post("/api/v2/secret-approvals", {
|
||||
environments,
|
||||
workspaceId,
|
||||
projectId,
|
||||
approvals,
|
||||
approvers,
|
||||
bypassers,
|
||||
@@ -33,9 +33,9 @@ export const useCreateSecretApprovalPolicy = () => {
|
||||
});
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { workspaceId }) => {
|
||||
onSuccess: (_, { projectId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: secretApprovalKeys.getApprovalPolicies(workspaceId)
|
||||
queryKey: secretApprovalKeys.getApprovalPolicies(projectId)
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -56,7 +56,7 @@ export const useUpdateSecretApprovalPolicy = () => {
|
||||
allowedSelfApprovals,
|
||||
environments
|
||||
}) => {
|
||||
const { data } = await apiRequest.patch(`/api/v1/secret-approvals/${id}`, {
|
||||
const { data } = await apiRequest.patch(`/api/v2/secret-approvals/${id}`, {
|
||||
approvals,
|
||||
approvers,
|
||||
bypassers,
|
||||
@@ -68,9 +68,9 @@ export const useUpdateSecretApprovalPolicy = () => {
|
||||
});
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { workspaceId }) => {
|
||||
onSuccess: (_, { projectId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: secretApprovalKeys.getApprovalPolicies(workspaceId)
|
||||
queryKey: secretApprovalKeys.getApprovalPolicies(projectId)
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -81,12 +81,12 @@ export const useDeleteSecretApprovalPolicy = () => {
|
||||
|
||||
return useMutation<object, object, TDeleteSecretPolicyDTO>({
|
||||
mutationFn: async ({ id }) => {
|
||||
const { data } = await apiRequest.delete(`/api/v1/secret-approvals/${id}`);
|
||||
const { data } = await apiRequest.delete(`/api/v2/secret-approvals/${id}`);
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { workspaceId }) => {
|
||||
onSuccess: (_, { projectId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: secretApprovalKeys.getApprovalPolicies(workspaceId)
|
||||
queryKey: secretApprovalKeys.getApprovalPolicies(projectId)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -10,54 +10,53 @@ import {
|
||||
} from "./types";
|
||||
|
||||
export const secretApprovalKeys = {
|
||||
getApprovalPolicies: (workspaceId: string) =>
|
||||
[{ workspaceId }, "secret-approval-policies"] as const,
|
||||
getApprovalPolicyOfABoard: (workspaceId: string, environment: string, secretPath: string) => [
|
||||
{ workspaceId, environment, secretPath },
|
||||
getApprovalPolicies: (projectId: string) => [{ projectId }, "secret-approval-policies"] as const,
|
||||
getApprovalPolicyOfABoard: (projectId: string, environment: string, secretPath: string) => [
|
||||
{ projectId, environment, secretPath },
|
||||
"Secret-approval-policy"
|
||||
]
|
||||
};
|
||||
|
||||
const fetchApprovalPolicies = async (workspaceId: string) => {
|
||||
const fetchApprovalPolicies = async (projectId: string) => {
|
||||
const { data } = await apiRequest.get<{ approvals: TSecretApprovalPolicy[] }>(
|
||||
"/api/v1/secret-approvals",
|
||||
{ params: { workspaceId } }
|
||||
"/api/v2/secret-approvals",
|
||||
{ params: { projectId } }
|
||||
);
|
||||
return data.approvals;
|
||||
};
|
||||
|
||||
export const useGetSecretApprovalPolicies = ({
|
||||
workspaceId,
|
||||
projectId,
|
||||
options = {}
|
||||
}: TGetSecretApprovalPoliciesDTO & TReactQueryOptions) =>
|
||||
useQuery({
|
||||
queryKey: secretApprovalKeys.getApprovalPolicies(workspaceId),
|
||||
queryFn: () => fetchApprovalPolicies(workspaceId),
|
||||
queryKey: secretApprovalKeys.getApprovalPolicies(projectId),
|
||||
queryFn: () => fetchApprovalPolicies(projectId),
|
||||
...options,
|
||||
enabled: Boolean(workspaceId) && (options?.enabled ?? true)
|
||||
enabled: Boolean(projectId) && (options?.enabled ?? true)
|
||||
});
|
||||
|
||||
const fetchApprovalPolicyOfABoard = async (
|
||||
workspaceId: string,
|
||||
projectId: string,
|
||||
environment: string,
|
||||
secretPath: string
|
||||
) => {
|
||||
const { data } = await apiRequest.get<{ policy: TSecretApprovalPolicy }>(
|
||||
"/api/v1/secret-approvals/board",
|
||||
{ params: { workspaceId, environment, secretPath } }
|
||||
"/api/v2/secret-approvals/board",
|
||||
{ params: { projectId, environment, secretPath } }
|
||||
);
|
||||
return data.policy || "";
|
||||
};
|
||||
|
||||
export const useGetSecretApprovalPolicyOfABoard = ({
|
||||
workspaceId,
|
||||
projectId,
|
||||
secretPath = "/",
|
||||
environment,
|
||||
options = {}
|
||||
}: TGetSecretApprovalPolicyOfBoardDTO & TReactQueryOptions) =>
|
||||
useQuery({
|
||||
queryKey: secretApprovalKeys.getApprovalPolicyOfABoard(workspaceId, environment, secretPath),
|
||||
queryFn: () => fetchApprovalPolicyOfABoard(workspaceId, environment, secretPath),
|
||||
queryKey: secretApprovalKeys.getApprovalPolicyOfABoard(projectId, environment, secretPath),
|
||||
queryFn: () => fetchApprovalPolicyOfABoard(projectId, environment, secretPath),
|
||||
...options,
|
||||
enabled: Boolean(workspaceId && secretPath && environment) && (options?.enabled ?? true)
|
||||
enabled: Boolean(projectId && secretPath && environment) && (options?.enabled ?? true)
|
||||
});
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { EnforcementLevel } from "../policies/enums";
|
||||
import { WorkspaceEnv } from "../workspace/types";
|
||||
import { ProjectEnv } from "../projects/types";
|
||||
|
||||
export type TSecretApprovalPolicy = {
|
||||
id: string;
|
||||
workspace: string;
|
||||
project: string;
|
||||
name: string;
|
||||
environments: WorkspaceEnv[];
|
||||
environments: ProjectEnv[];
|
||||
secretPath?: string;
|
||||
approvals: number;
|
||||
approvers: Approver[];
|
||||
@@ -36,17 +36,17 @@ export type Bypasser = {
|
||||
};
|
||||
|
||||
export type TGetSecretApprovalPoliciesDTO = {
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
};
|
||||
|
||||
export type TGetSecretApprovalPolicyOfBoardDTO = {
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
};
|
||||
|
||||
export type TCreateSecretPolicyDTO = {
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
name?: string;
|
||||
environments: string[];
|
||||
secretPath: string;
|
||||
@@ -67,12 +67,12 @@ export type TUpdateSecretPolicyDTO = {
|
||||
allowedSelfApprovals?: boolean;
|
||||
enforcementLevel?: EnforcementLevel;
|
||||
// for invalidating list
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
environments?: string[];
|
||||
};
|
||||
|
||||
export type TDeleteSecretPolicyDTO = {
|
||||
id: string;
|
||||
// for invalidating list
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
};
|
||||
|
||||
@@ -36,9 +36,9 @@ export const useUpdateSecretApprovalRequestStatus = () => {
|
||||
});
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { id, workspaceId }) => {
|
||||
onSuccess: (_, { id, projectId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.detail({ id }) });
|
||||
queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.count({ workspaceId }) });
|
||||
queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.count({ projectId }) });
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -53,10 +53,10 @@ export const usePerformSecretApprovalRequestMerge = () => {
|
||||
});
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { id, workspaceId }) => {
|
||||
onSuccess: (_, { id, projectId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.detail({ id }) });
|
||||
queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.list({ workspaceId }) });
|
||||
queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.count({ workspaceId }) });
|
||||
queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.list({ projectId }) });
|
||||
queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.count({ projectId }) });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
|
||||
export const secretApprovalRequestKeys = {
|
||||
list: ({
|
||||
workspaceId,
|
||||
projectId,
|
||||
environment,
|
||||
status,
|
||||
committer,
|
||||
@@ -23,20 +23,20 @@ export const secretApprovalRequestKeys = {
|
||||
search
|
||||
}: TGetSecretApprovalRequestList) =>
|
||||
[
|
||||
{ workspaceId, environment, status, committer, offset, limit, search },
|
||||
{ projectId, environment, status, committer, offset, limit, search },
|
||||
"secret-approval-requests"
|
||||
] as const,
|
||||
detail: ({ id }: Omit<TGetSecretApprovalRequestDetails, "decryptKey">) =>
|
||||
[{ id }, "secret-approval-request-detail"] as const,
|
||||
count: ({ workspaceId, policyId }: TGetSecretApprovalRequestCount) => [
|
||||
{ workspaceId },
|
||||
count: ({ projectId, policyId }: TGetSecretApprovalRequestCount) => [
|
||||
{ projectId },
|
||||
"secret-approval-request-count",
|
||||
...(policyId ? [policyId] : [])
|
||||
]
|
||||
};
|
||||
|
||||
const fetchSecretApprovalRequestList = async ({
|
||||
workspaceId,
|
||||
projectId,
|
||||
environment,
|
||||
committer,
|
||||
status = "open",
|
||||
@@ -49,7 +49,7 @@ const fetchSecretApprovalRequestList = async ({
|
||||
totalCount: number;
|
||||
}>("/api/v1/secret-approval-requests", {
|
||||
params: {
|
||||
workspaceId,
|
||||
projectId,
|
||||
environment,
|
||||
committer,
|
||||
status,
|
||||
@@ -63,7 +63,7 @@ const fetchSecretApprovalRequestList = async ({
|
||||
};
|
||||
|
||||
export const useGetSecretApprovalRequests = ({
|
||||
workspaceId,
|
||||
projectId,
|
||||
environment,
|
||||
options = {},
|
||||
status,
|
||||
@@ -74,7 +74,7 @@ export const useGetSecretApprovalRequests = ({
|
||||
}: TGetSecretApprovalRequestList & TReactQueryOptions) =>
|
||||
useQuery({
|
||||
queryKey: secretApprovalRequestKeys.list({
|
||||
workspaceId,
|
||||
projectId,
|
||||
environment,
|
||||
committer,
|
||||
status,
|
||||
@@ -84,7 +84,7 @@ export const useGetSecretApprovalRequests = ({
|
||||
}),
|
||||
queryFn: () =>
|
||||
fetchSecretApprovalRequestList({
|
||||
workspaceId,
|
||||
projectId,
|
||||
environment,
|
||||
status,
|
||||
committer,
|
||||
@@ -92,7 +92,7 @@ export const useGetSecretApprovalRequests = ({
|
||||
offset,
|
||||
search
|
||||
}),
|
||||
enabled: Boolean(workspaceId) && (options?.enabled ?? true),
|
||||
enabled: Boolean(projectId) && (options?.enabled ?? true),
|
||||
placeholderData: (previousData) => previousData
|
||||
});
|
||||
|
||||
@@ -127,19 +127,19 @@ export const useGetSecretApprovalRequestDetails = ({
|
||||
});
|
||||
|
||||
const fetchSecretApprovalRequestCount = async ({
|
||||
workspaceId,
|
||||
projectId,
|
||||
policyId
|
||||
}: TGetSecretApprovalRequestCount) => {
|
||||
const { data } = await apiRequest.get<{ approvals: TSecretApprovalRequestCount }>(
|
||||
"/api/v1/secret-approval-requests/count",
|
||||
{ params: { workspaceId, policyId } }
|
||||
{ params: { projectId, policyId } }
|
||||
);
|
||||
|
||||
return data.approvals;
|
||||
};
|
||||
|
||||
export const useGetSecretApprovalRequestCount = ({
|
||||
workspaceId,
|
||||
projectId,
|
||||
policyId,
|
||||
options = {}
|
||||
}: TGetSecretApprovalRequestCount & {
|
||||
@@ -154,8 +154,8 @@ export const useGetSecretApprovalRequestCount = ({
|
||||
>;
|
||||
}) =>
|
||||
useQuery({
|
||||
queryKey: secretApprovalRequestKeys.count({ workspaceId, policyId }),
|
||||
queryKey: secretApprovalRequestKeys.count({ projectId, policyId }),
|
||||
refetchInterval: 15000,
|
||||
queryFn: () => fetchSecretApprovalRequestCount({ workspaceId, policyId }),
|
||||
enabled: Boolean(workspaceId) && (options?.enabled ?? true)
|
||||
queryFn: () => fetchSecretApprovalRequestCount({ projectId, policyId }),
|
||||
enabled: Boolean(projectId) && (options?.enabled ?? true)
|
||||
});
|
||||
|
||||
@@ -55,7 +55,7 @@ export type TSecretApprovalRequest = {
|
||||
username: string;
|
||||
isOrgMembershipActive: boolean;
|
||||
}[];
|
||||
workspace: string;
|
||||
project: string;
|
||||
environment: string;
|
||||
folderId: string;
|
||||
secretPath: string;
|
||||
@@ -110,7 +110,7 @@ export type TSecretApprovalRequestCount = {
|
||||
};
|
||||
|
||||
export type TGetSecretApprovalRequestList = {
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
environment?: string;
|
||||
status?: "open" | "close";
|
||||
committer?: string;
|
||||
@@ -120,7 +120,7 @@ export type TGetSecretApprovalRequestList = {
|
||||
};
|
||||
|
||||
export type TGetSecretApprovalRequestCount = {
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
policyId?: string;
|
||||
};
|
||||
|
||||
@@ -137,11 +137,11 @@ export type TUpdateSecretApprovalReviewStatusDTO = {
|
||||
export type TUpdateSecretApprovalRequestStatusDTO = {
|
||||
status: "open" | "close";
|
||||
id: string;
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
};
|
||||
|
||||
export type TPerformSecretApprovalRequestMerge = {
|
||||
id: string;
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
bypassReason?: string;
|
||||
};
|
||||
|
||||
@@ -30,10 +30,10 @@ export const folderQueryKeys = {
|
||||
["secret-folders", "environment", projectId] as const
|
||||
};
|
||||
|
||||
const fetchProjectFolders = async (workspaceId: string, environment: string, path = "/") => {
|
||||
const { data } = await apiRequest.get<{ folders: TSecretFolder[] }>("/api/v1/folders", {
|
||||
const fetchProjectFolders = async (projectId: string, environment: string, path = "/") => {
|
||||
const { data } = await apiRequest.get<{ folders: TSecretFolder[] }>("/api/v2/folders", {
|
||||
params: {
|
||||
workspaceId,
|
||||
projectId,
|
||||
environment,
|
||||
path
|
||||
}
|
||||
@@ -57,7 +57,7 @@ export const useListProjectEnvironmentsFolders = (
|
||||
queryKey: folderQueryKeys.getProjectEnvironmentsFolders(projectId),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<TProjectEnvironmentsFolders>(
|
||||
`/api/v1/workspace/${projectId}/environment-folder-tree`
|
||||
`/api/v1/projects/${projectId}/environment-folder-tree`
|
||||
);
|
||||
return data;
|
||||
},
|
||||
@@ -145,9 +145,9 @@ export const useCreateFolder = () => {
|
||||
|
||||
return useMutation<object, object, TCreateFolderDTO>({
|
||||
mutationFn: async (dto) => {
|
||||
const { data } = await apiRequest.post("/api/v1/folders", {
|
||||
const { data } = await apiRequest.post("/api/v2/folders", {
|
||||
...dto,
|
||||
workspaceId: dto.projectId
|
||||
projectId: dto.projectId
|
||||
});
|
||||
return data;
|
||||
},
|
||||
@@ -162,13 +162,13 @@ export const useCreateFolder = () => {
|
||||
queryKey: folderQueryKeys.getSecretFolders({ projectId, environment, path })
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: secretSnapshotKeys.list({ workspaceId: projectId, environment, directory: path })
|
||||
queryKey: secretSnapshotKeys.list({ projectId, environment, directory: path })
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: secretSnapshotKeys.count({ workspaceId: projectId, environment, directory: path })
|
||||
queryKey: secretSnapshotKeys.count({ projectId, environment, directory: path })
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: commitKeys.count({ workspaceId: projectId, environment, directory: path })
|
||||
queryKey: commitKeys.count({ projectId, environment, directory: path })
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -179,10 +179,10 @@ export const useUpdateFolder = () => {
|
||||
|
||||
return useMutation<object, object, TUpdateFolderDTO>({
|
||||
mutationFn: async ({ path = "/", folderId, name, environment, projectId, description }) => {
|
||||
const { data } = await apiRequest.patch(`/api/v1/folders/${folderId}`, {
|
||||
const { data } = await apiRequest.patch(`/api/v2/folders/${folderId}`, {
|
||||
name,
|
||||
environment,
|
||||
workspaceId: projectId,
|
||||
projectId,
|
||||
path,
|
||||
description
|
||||
});
|
||||
@@ -199,16 +199,16 @@ export const useUpdateFolder = () => {
|
||||
queryKey: folderQueryKeys.getSecretFolders({ projectId, environment, path })
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: secretSnapshotKeys.list({ workspaceId: projectId, environment, directory: path })
|
||||
queryKey: secretSnapshotKeys.list({ projectId, environment, directory: path })
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: secretSnapshotKeys.count({ workspaceId: projectId, environment, directory: path })
|
||||
queryKey: secretSnapshotKeys.count({ projectId, environment, directory: path })
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: commitKeys.count({ workspaceId: projectId, environment, directory: path })
|
||||
queryKey: commitKeys.count({ projectId, environment, directory: path })
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: commitKeys.history({ workspaceId: projectId, environment, directory: path })
|
||||
queryKey: commitKeys.history({ projectId, environment, directory: path })
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -219,10 +219,10 @@ export const useDeleteFolder = () => {
|
||||
|
||||
return useMutation<object, object, TDeleteFolderDTO>({
|
||||
mutationFn: async ({ path = "/", folderId, environment, projectId }) => {
|
||||
const { data } = await apiRequest.delete(`/api/v1/folders/${folderId}`, {
|
||||
const { data } = await apiRequest.delete(`/api/v2/folders/${folderId}`, {
|
||||
data: {
|
||||
environment,
|
||||
workspaceId: projectId,
|
||||
projectId,
|
||||
path
|
||||
}
|
||||
});
|
||||
@@ -239,16 +239,16 @@ export const useDeleteFolder = () => {
|
||||
queryKey: folderQueryKeys.getSecretFolders({ projectId, environment, path })
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: secretSnapshotKeys.list({ workspaceId: projectId, environment, directory: path })
|
||||
queryKey: secretSnapshotKeys.list({ projectId, environment, directory: path })
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: secretSnapshotKeys.count({ workspaceId: projectId, environment, directory: path })
|
||||
queryKey: secretSnapshotKeys.count({ projectId, environment, directory: path })
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: commitKeys.count({ workspaceId: projectId, environment, directory: path })
|
||||
queryKey: commitKeys.count({ projectId, environment, directory: path })
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: commitKeys.history({ workspaceId: projectId, environment, directory: path })
|
||||
queryKey: commitKeys.history({ projectId, environment, directory: path })
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -258,9 +258,9 @@ export const useUpdateFolderBatch = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<object, object, TUpdateFolderBatchDTO>({
|
||||
mutationFn: async ({ projectSlug, folders }) => {
|
||||
const { data } = await apiRequest.patch("/api/v1/folders/batch", {
|
||||
projectSlug,
|
||||
mutationFn: async ({ projectId, folders }) => {
|
||||
const { data } = await apiRequest.patch("/api/v2/folders/batch", {
|
||||
projectId,
|
||||
folders
|
||||
});
|
||||
|
||||
@@ -283,28 +283,28 @@ export const useUpdateFolderBatch = () => {
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: secretSnapshotKeys.list({
|
||||
workspaceId: projectId,
|
||||
projectId,
|
||||
environment: folder.environment,
|
||||
directory: folder.path
|
||||
})
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: secretSnapshotKeys.count({
|
||||
workspaceId: projectId,
|
||||
projectId,
|
||||
environment: folder.environment,
|
||||
directory: folder.path
|
||||
})
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: commitKeys.count({
|
||||
workspaceId: projectId,
|
||||
projectId,
|
||||
environment: folder.environment,
|
||||
directory: folder.path
|
||||
})
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: commitKeys.history({
|
||||
workspaceId: projectId,
|
||||
projectId,
|
||||
environment: folder.environment,
|
||||
directory: folder.path
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { WorkspaceEnv } from "@app/hooks/api/workspace/types";
|
||||
import { ProjectEnv } from "@app/hooks/api/projects/types";
|
||||
|
||||
export enum ReservedFolders {
|
||||
SecretReplication = "__reserve_replication_"
|
||||
@@ -22,7 +22,7 @@ export type TSecretFolder = {
|
||||
export type TSecretFolderWithPath = TSecretFolder & { path: string };
|
||||
|
||||
export type TProjectEnvironmentsFolders = {
|
||||
[key: string]: WorkspaceEnv & { folders: TSecretFolderWithPath[] };
|
||||
[key: string]: ProjectEnv & { folders: TSecretFolderWithPath[] };
|
||||
};
|
||||
|
||||
export type TGetProjectFoldersDTO = {
|
||||
@@ -63,7 +63,6 @@ export type TDeleteFolderDTO = {
|
||||
|
||||
export type TUpdateFolderBatchDTO = {
|
||||
projectId: string;
|
||||
projectSlug: string;
|
||||
folders: {
|
||||
name: string;
|
||||
environment: string;
|
||||
|
||||
@@ -16,10 +16,10 @@ export const useCreateSecretImport = () => {
|
||||
|
||||
return useMutation<object, object, TCreateSecretImportDTO>({
|
||||
mutationFn: async ({ import: secretImport, environment, isReplication, projectId, path }) => {
|
||||
const { data } = await apiRequest.post("/api/v1/secret-imports", {
|
||||
const { data } = await apiRequest.post("/api/v2/secret-imports", {
|
||||
import: secretImport,
|
||||
environment,
|
||||
workspaceId: projectId,
|
||||
projectId,
|
||||
path,
|
||||
isReplication
|
||||
});
|
||||
@@ -44,11 +44,11 @@ export const useUpdateSecretImport = () => {
|
||||
|
||||
return useMutation<object, object, TUpdateSecretImportDTO>({
|
||||
mutationFn: async ({ environment, import: secretImports, projectId, path, id }) => {
|
||||
const { data } = await apiRequest.patch(`/api/v1/secret-imports/${id}`, {
|
||||
const { data } = await apiRequest.patch(`/api/v2/secret-imports/${id}`, {
|
||||
import: secretImports,
|
||||
environment,
|
||||
path,
|
||||
workspaceId: projectId
|
||||
projectId
|
||||
});
|
||||
return data;
|
||||
},
|
||||
@@ -69,10 +69,10 @@ export const useUpdateSecretImport = () => {
|
||||
export const useResyncSecretReplication = () => {
|
||||
return useMutation<object, object, TResyncSecretReplicationDTO>({
|
||||
mutationFn: async ({ environment, projectId, path, id }) => {
|
||||
const { data } = await apiRequest.post(`/api/v1/secret-imports/${id}/replication-resync`, {
|
||||
const { data } = await apiRequest.post(`/api/v2/secret-imports/${id}/replication-resync`, {
|
||||
environment,
|
||||
path,
|
||||
workspaceId: projectId
|
||||
projectId
|
||||
});
|
||||
return data;
|
||||
}
|
||||
@@ -84,9 +84,9 @@ export const useDeleteSecretImport = () => {
|
||||
|
||||
return useMutation<object, object, TDeleteSecretImportDTO>({
|
||||
mutationFn: async ({ id, projectId, path, environment }) => {
|
||||
const { data } = await apiRequest.delete(`/api/v1/secret-imports/${id}`, {
|
||||
const { data } = await apiRequest.delete(`/api/v2/secret-imports/${id}`, {
|
||||
data: {
|
||||
workspaceId: projectId,
|
||||
projectId,
|
||||
path,
|
||||
environment
|
||||
}
|
||||
|
||||
@@ -30,10 +30,10 @@ export const secretImportKeys = {
|
||||
|
||||
const fetchSecretImport = async ({ projectId, environment, path = "/" }: TGetSecretImports) => {
|
||||
const { data } = await apiRequest.get<{ secretImports: TSecretImport[] }>(
|
||||
"/api/v1/secret-imports",
|
||||
"/api/v2/secret-imports",
|
||||
{
|
||||
params: {
|
||||
workspaceId: projectId,
|
||||
projectId,
|
||||
environment,
|
||||
path
|
||||
}
|
||||
@@ -65,16 +65,12 @@ export const useGetSecretImports = ({
|
||||
queryFn: () => fetchSecretImport({ path, projectId, environment })
|
||||
});
|
||||
|
||||
const fetchImportedSecrets = async (
|
||||
workspaceId: string,
|
||||
environment: string,
|
||||
directory?: string
|
||||
) => {
|
||||
const fetchImportedSecrets = async (projectId: string, environment: string, directory?: string) => {
|
||||
const { data } = await apiRequest.get<{ secrets: TImportedSecrets[] }>(
|
||||
"/api/v1/secret-imports/secrets/raw",
|
||||
"/api/v1/dashboard/secret-imports",
|
||||
{
|
||||
params: {
|
||||
workspaceId,
|
||||
projectId,
|
||||
environment,
|
||||
path: directory
|
||||
}
|
||||
@@ -89,10 +85,10 @@ const fetchImportedFolders = async ({
|
||||
path
|
||||
}: TGetImportedFoldersByEnvDTO) => {
|
||||
const { data } = await apiRequest.get<{ secretImports: TSecretImport[] }>(
|
||||
"/api/v1/secret-imports",
|
||||
"/api/v2/secret-imports",
|
||||
{
|
||||
params: {
|
||||
workspaceId: projectId,
|
||||
projectId,
|
||||
environment,
|
||||
path
|
||||
}
|
||||
@@ -136,13 +132,13 @@ export const useGetImportedSecretsSingleEnv = ({
|
||||
id: encSecret.id,
|
||||
env: encSecret.environment,
|
||||
key: encSecret.secretKey,
|
||||
value: encSecret.secretValue,
|
||||
secretValueHidden: encSecret.secretValueHidden,
|
||||
tags: encSecret.tags,
|
||||
comment: encSecret.secretComment,
|
||||
createdAt: encSecret.createdAt,
|
||||
updatedAt: encSecret.updatedAt,
|
||||
version: encSecret.version
|
||||
version: encSecret.version,
|
||||
isEmpty: encSecret.isEmpty
|
||||
};
|
||||
})
|
||||
}));
|
||||
@@ -176,7 +172,6 @@ export const useGetImportedSecretsAllEnvs = ({
|
||||
id: encSecret.id,
|
||||
env: encSecret.environment,
|
||||
key: encSecret.secretKey,
|
||||
value: encSecret.secretValue,
|
||||
secretValueHidden: encSecret.secretValueHidden,
|
||||
tags: encSecret.tags,
|
||||
comment: encSecret.secretComment,
|
||||
@@ -237,7 +232,9 @@ export const useGetImportedSecretsAllEnvs = ({
|
||||
|
||||
return {
|
||||
secret: secret?.secrets.find((s) => s.key === secretName),
|
||||
environmentInfo: secret?.environmentInfo
|
||||
environmentInfo: secret?.environmentInfo,
|
||||
secretPath: secret?.secretPath,
|
||||
environment: secret?.environment
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { ProjectEnv } from "../projects/types";
|
||||
import { SecretV3Raw } from "../secrets/types";
|
||||
import { WorkspaceEnv } from "../workspace/types";
|
||||
|
||||
export type TSecretImport = {
|
||||
id: string;
|
||||
folderId: string;
|
||||
importPath: string;
|
||||
importEnv: WorkspaceEnv;
|
||||
importEnv: ProjectEnv;
|
||||
position: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
@@ -25,10 +25,10 @@ export type TGetImportedFoldersByEnvDTO = {
|
||||
|
||||
export type TImportedSecrets = {
|
||||
environment: string;
|
||||
environmentInfo: WorkspaceEnv;
|
||||
environmentInfo: ProjectEnv;
|
||||
secretPath: string;
|
||||
folderId: string;
|
||||
secrets: SecretV3Raw[];
|
||||
secrets: Omit<SecretV3Raw, "secretValue">[];
|
||||
};
|
||||
|
||||
export type TGetSecretImports = {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user