mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 18:27:36 +00:00
Merge pull request #3241 from Infisical/feat/addDynamicSecretsToOverview
Add dynamic secrets modal form on secrets overview page
This commit is contained in:
@@ -19,6 +19,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||||||
import { Link, useNavigate, useRouter, useSearch } from "@tanstack/react-router";
|
import { Link, useNavigate, useRouter, useSearch } from "@tanstack/react-router";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
|
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||||
import {
|
import {
|
||||||
@@ -49,8 +50,10 @@ import {
|
|||||||
import { ROUTE_PATHS } from "@app/const/routes";
|
import { ROUTE_PATHS } from "@app/const/routes";
|
||||||
import {
|
import {
|
||||||
ProjectPermissionActions,
|
ProjectPermissionActions,
|
||||||
|
ProjectPermissionDynamicSecretActions,
|
||||||
ProjectPermissionSub,
|
ProjectPermissionSub,
|
||||||
useProjectPermission,
|
useProjectPermission,
|
||||||
|
useSubscription,
|
||||||
useWorkspace
|
useWorkspace
|
||||||
} from "@app/context";
|
} from "@app/context";
|
||||||
import { useDebounce, usePagination, usePopUp, useResetPageHelper } from "@app/hooks";
|
import { useDebounce, usePagination, usePopUp, useResetPageHelper } from "@app/hooks";
|
||||||
@@ -71,6 +74,7 @@ import { SecretType, SecretV3RawSanitized, TSecretFolder } from "@app/hooks/api/
|
|||||||
import { ProjectType, ProjectVersion } from "@app/hooks/api/workspace/types";
|
import { ProjectType, ProjectVersion } from "@app/hooks/api/workspace/types";
|
||||||
import { useDynamicSecretOverview, useFolderOverview, useSecretOverview } from "@app/hooks/utils";
|
import { useDynamicSecretOverview, useFolderOverview, useSecretOverview } from "@app/hooks/utils";
|
||||||
|
|
||||||
|
import { CreateDynamicSecretForm } from "../SecretDashboardPage/components/ActionBar/CreateDynamicSecretForm";
|
||||||
import { FolderForm } from "../SecretDashboardPage/components/ActionBar/FolderForm";
|
import { FolderForm } from "../SecretDashboardPage/components/ActionBar/FolderForm";
|
||||||
import { CreateSecretForm } from "./components/CreateSecretForm";
|
import { CreateSecretForm } from "./components/CreateSecretForm";
|
||||||
import { FolderBreadCrumbs } from "./components/FolderBreadCrumbs";
|
import { FolderBreadCrumbs } from "./components/FolderBreadCrumbs";
|
||||||
@@ -131,6 +135,7 @@ export const OverviewPage = () => {
|
|||||||
const [searchFilter, setSearchFilter] = useState("");
|
const [searchFilter, setSearchFilter] = useState("");
|
||||||
const [debouncedSearchFilter, setDebouncedSearchFilter] = useDebounce(searchFilter);
|
const [debouncedSearchFilter, setDebouncedSearchFilter] = useDebounce(searchFilter);
|
||||||
const secretPath = (routerSearch?.secretPath as string) || "/";
|
const secretPath = (routerSearch?.secretPath as string) || "/";
|
||||||
|
const { subscription } = useSubscription();
|
||||||
|
|
||||||
const [filter, setFilter] = useState<Filter>(DEFAULT_FILTER_STATE);
|
const [filter, setFilter] = useState<Filter>(DEFAULT_FILTER_STATE);
|
||||||
const [filterHistory, setFilterHistory] = useState<
|
const [filterHistory, setFilterHistory] = useState<
|
||||||
@@ -178,6 +183,15 @@ export const OverviewPage = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const userAvailableEnvs = currentWorkspace?.environments || [];
|
const userAvailableEnvs = currentWorkspace?.environments || [];
|
||||||
|
const userAvailableDynamicSecretEnvs = userAvailableEnvs.filter((env) =>
|
||||||
|
permission.can(
|
||||||
|
ProjectPermissionDynamicSecretActions.CreateRootCredential,
|
||||||
|
subject(ProjectPermissionSub.DynamicSecrets, {
|
||||||
|
environment: env.slug,
|
||||||
|
secretPath
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
const [visibleEnvs, setVisibleEnvs] = useState(userAvailableEnvs);
|
const [visibleEnvs, setVisibleEnvs] = useState(userAvailableEnvs);
|
||||||
|
|
||||||
@@ -249,7 +263,9 @@ export const OverviewPage = () => {
|
|||||||
"addSecretsInAllEnvs",
|
"addSecretsInAllEnvs",
|
||||||
"addFolder",
|
"addFolder",
|
||||||
"misc",
|
"misc",
|
||||||
"updateFolder"
|
"updateFolder",
|
||||||
|
"addDynamicSecret",
|
||||||
|
"upgradePlan"
|
||||||
] as const);
|
] as const);
|
||||||
|
|
||||||
const handleFolderCreate = async (folderName: string, description: string | null) => {
|
const handleFolderCreate = async (folderName: string, description: string | null) => {
|
||||||
@@ -851,20 +867,43 @@ export const OverviewPage = () => {
|
|||||||
>
|
>
|
||||||
{(isAllowed) => (
|
{(isAllowed) => (
|
||||||
<Button
|
<Button
|
||||||
leftIcon={<FontAwesomeIcon icon={faFolderPlus} />}
|
leftIcon={<FontAwesomeIcon icon={faFolderPlus} className="pr-2" />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handlePopUpOpen("addFolder");
|
handlePopUpOpen("addFolder");
|
||||||
handlePopUpClose("misc");
|
handlePopUpClose("misc");
|
||||||
}}
|
}}
|
||||||
isDisabled={!isAllowed}
|
isDisabled={!isAllowed}
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
className="h-10"
|
className="h-10 text-left"
|
||||||
isFullWidth
|
isFullWidth
|
||||||
>
|
>
|
||||||
Add Folder
|
Add Folder
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</ProjectPermissionCan>
|
</ProjectPermissionCan>
|
||||||
|
<Tooltip
|
||||||
|
content={
|
||||||
|
userAvailableDynamicSecretEnvs.length === 0 ? "Access restricted" : ""
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faFingerprint} className="pr-2" />}
|
||||||
|
onClick={() => {
|
||||||
|
if (subscription?.dynamicSecret) {
|
||||||
|
handlePopUpOpen("addDynamicSecret");
|
||||||
|
handlePopUpClose("misc");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
isDisabled={userAvailableDynamicSecretEnvs.length === 0}
|
||||||
|
variant="outline_bg"
|
||||||
|
className="h-10 text-left"
|
||||||
|
isFullWidth
|
||||||
|
>
|
||||||
|
Add Dynamic Secret
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
@@ -1170,6 +1209,24 @@ export const OverviewPage = () => {
|
|||||||
/>
|
/>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
<CreateDynamicSecretForm
|
||||||
|
isOpen={popUp.addDynamicSecret.isOpen}
|
||||||
|
onToggle={(isOpen) => handlePopUpToggle("addDynamicSecret", isOpen)}
|
||||||
|
projectSlug={projectSlug}
|
||||||
|
environments={userAvailableDynamicSecretEnvs}
|
||||||
|
secretPath={secretPath}
|
||||||
|
/>
|
||||||
|
{subscription && (
|
||||||
|
<UpgradePlanModal
|
||||||
|
isOpen={popUp.upgradePlan.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||||
|
text={
|
||||||
|
subscription.slug === null
|
||||||
|
? "You can perform this action under an Enterprise license"
|
||||||
|
: "You can perform this action if you switch to Infisical's Team plan"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+2
-1
@@ -655,8 +655,9 @@ export const ActionBar = ({
|
|||||||
isOpen={popUp.addDynamicSecret.isOpen}
|
isOpen={popUp.addDynamicSecret.isOpen}
|
||||||
onToggle={(isOpen) => handlePopUpToggle("addDynamicSecret", isOpen)}
|
onToggle={(isOpen) => handlePopUpToggle("addDynamicSecret", isOpen)}
|
||||||
projectSlug={projectSlug}
|
projectSlug={projectSlug}
|
||||||
environment={environment}
|
environments={[{ slug: environment, name: environment, id: "not-used" }]}
|
||||||
secretPath={secretPath}
|
secretPath={secretPath}
|
||||||
|
isSingleEnvironmentMode
|
||||||
/>
|
/>
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={popUp.addFolder.isOpen}
|
isOpen={popUp.addFolder.isOpen}
|
||||||
|
|||||||
+41
-7
@@ -11,6 +11,7 @@ import {
|
|||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionTrigger,
|
AccordionTrigger,
|
||||||
Button,
|
Button,
|
||||||
|
FilterableSelect,
|
||||||
FormControl,
|
FormControl,
|
||||||
Input,
|
Input,
|
||||||
SecretInput,
|
SecretInput,
|
||||||
@@ -18,6 +19,7 @@ import {
|
|||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||||
|
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
provider: z.object({
|
provider: z.object({
|
||||||
@@ -50,7 +52,8 @@ const formSchema = z.object({
|
|||||||
if (valMs > 24 * 60 * 60 * 1000)
|
if (valMs > 24 * 60 * 60 * 1000)
|
||||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||||
}),
|
}),
|
||||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||||
|
environment: z.object({ name: z.string(), slug: z.string() })
|
||||||
});
|
});
|
||||||
type TForm = z.infer<typeof formSchema>;
|
type TForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
@@ -59,15 +62,17 @@ type Props = {
|
|||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
environment: string;
|
environments: WorkspaceEnv[];
|
||||||
|
isSingleEnvironmentMode?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AwsElastiCacheInputForm = ({
|
export const AwsElastiCacheInputForm = ({
|
||||||
onCompleted,
|
onCompleted,
|
||||||
onCancel,
|
onCancel,
|
||||||
environment,
|
environments,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectSlug
|
projectSlug,
|
||||||
|
isSingleEnvironmentMode
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -87,13 +92,20 @@ export const AwsElastiCacheInputForm = ({
|
|||||||
revocationStatement: `{
|
revocationStatement: `{
|
||||||
"UserId": "{{username}}"
|
"UserId": "{{username}}"
|
||||||
}`
|
}`
|
||||||
}
|
},
|
||||||
|
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const createDynamicSecret = useCreateDynamicSecret();
|
const createDynamicSecret = useCreateDynamicSecret();
|
||||||
|
|
||||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
const handleCreateDynamicSecret = async ({
|
||||||
|
name,
|
||||||
|
maxTTL,
|
||||||
|
provider,
|
||||||
|
defaultTTL,
|
||||||
|
environment
|
||||||
|
}: TForm) => {
|
||||||
// wait till previous request is finished
|
// wait till previous request is finished
|
||||||
if (createDynamicSecret.isPending) return;
|
if (createDynamicSecret.isPending) return;
|
||||||
try {
|
try {
|
||||||
@@ -104,7 +116,7 @@ export const AwsElastiCacheInputForm = ({
|
|||||||
path: secretPath,
|
path: secretPath,
|
||||||
defaultTTL,
|
defaultTTL,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environmentSlug: environment
|
environmentSlug: environment.slug
|
||||||
});
|
});
|
||||||
onCompleted();
|
onCompleted();
|
||||||
} catch {
|
} catch {
|
||||||
@@ -299,6 +311,28 @@ export const AwsElastiCacheInputForm = ({
|
|||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
{!isSingleEnvironmentMode && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="environment"
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Environment"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<FilterableSelect
|
||||||
|
options={environments}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
placeholder="Select the environment to create secret in..."
|
||||||
|
getOptionLabel={(option) => option.name}
|
||||||
|
getOptionValue={(option) => option.slug}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+45
-8
@@ -5,9 +5,10 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { TtlFormLabel } from "@app/components/features";
|
import { TtlFormLabel } from "@app/components/features";
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, Input, TextArea } from "@app/components/v2";
|
import { Button, FilterableSelect, FormControl, Input, TextArea } from "@app/components/v2";
|
||||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||||
|
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
provider: z.object({
|
provider: z.object({
|
||||||
@@ -40,7 +41,8 @@ const formSchema = z.object({
|
|||||||
if (valMs > 24 * 60 * 60 * 1000)
|
if (valMs > 24 * 60 * 60 * 1000)
|
||||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||||
}),
|
}),
|
||||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||||
|
environment: z.object({ name: z.string(), slug: z.string() })
|
||||||
});
|
});
|
||||||
type TForm = z.infer<typeof formSchema>;
|
type TForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
@@ -49,29 +51,41 @@ type Props = {
|
|||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
environment: string;
|
environments: WorkspaceEnv[];
|
||||||
|
isSingleEnvironmentMode?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AwsIamInputForm = ({
|
export const AwsIamInputForm = ({
|
||||||
onCompleted,
|
onCompleted,
|
||||||
onCancel,
|
onCancel,
|
||||||
environment,
|
environments,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectSlug
|
projectSlug,
|
||||||
|
isSingleEnvironmentMode
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
formState: { isSubmitting },
|
formState: { isSubmitting },
|
||||||
handleSubmit
|
handleSubmit
|
||||||
} = useForm<TForm>({
|
} = useForm<TForm>({
|
||||||
resolver: zodResolver(formSchema)
|
resolver: zodResolver(formSchema),
|
||||||
|
defaultValues: {
|
||||||
|
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const createDynamicSecret = useCreateDynamicSecret();
|
const createDynamicSecret = useCreateDynamicSecret();
|
||||||
|
|
||||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
const handleCreateDynamicSecret = async ({
|
||||||
|
name,
|
||||||
|
maxTTL,
|
||||||
|
provider,
|
||||||
|
defaultTTL,
|
||||||
|
environment
|
||||||
|
}: TForm) => {
|
||||||
// wait till previous request is finished
|
// wait till previous request is finished
|
||||||
if (createDynamicSecret.isPending) return;
|
if (createDynamicSecret.isPending) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await createDynamicSecret.mutateAsync({
|
await createDynamicSecret.mutateAsync({
|
||||||
provider: { type: DynamicSecretProviders.AwsIam, inputs: provider },
|
provider: { type: DynamicSecretProviders.AwsIam, inputs: provider },
|
||||||
@@ -80,7 +94,7 @@ export const AwsIamInputForm = ({
|
|||||||
path: secretPath,
|
path: secretPath,
|
||||||
defaultTTL,
|
defaultTTL,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environmentSlug: environment
|
environmentSlug: environment.slug
|
||||||
});
|
});
|
||||||
onCompleted();
|
onCompleted();
|
||||||
} catch {
|
} catch {
|
||||||
@@ -286,6 +300,29 @@ export const AwsIamInputForm = ({
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
{!isSingleEnvironmentMode && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="environment"
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Environment"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<FilterableSelect
|
||||||
|
options={environments}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
placeholder="Select the environment to create secret in..."
|
||||||
|
getOptionLabel={(option) => option.name}
|
||||||
|
getOptionValue={(option) => option.slug}
|
||||||
|
menuPlacement="top"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+39
-8
@@ -12,7 +12,7 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { TtlFormLabel } from "@app/components/features";
|
import { TtlFormLabel } from "@app/components/features";
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, Input } from "@app/components/v2";
|
import { Button, FilterableSelect, FormControl, Input } from "@app/components/v2";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
@@ -23,6 +23,7 @@ import { Tooltip } from "@app/components/v2/Tooltip";
|
|||||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||||
import { useGetDynamicSecretProviderData } from "@app/hooks/api/dynamicSecret/queries";
|
import { useGetDynamicSecretProviderData } from "@app/hooks/api/dynamicSecret/queries";
|
||||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||||
|
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
selectedUsers: z.array(
|
selectedUsers: z.array(
|
||||||
@@ -60,7 +61,8 @@ const formSchema = z.object({
|
|||||||
name: z
|
name: z
|
||||||
.string()
|
.string()
|
||||||
.min(1)
|
.min(1)
|
||||||
.refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
.refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||||
|
environment: z.object({ name: z.string(), slug: z.string() })
|
||||||
});
|
});
|
||||||
type TForm = z.infer<typeof formSchema>;
|
type TForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
@@ -69,15 +71,17 @@ type Props = {
|
|||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
environment: string;
|
environments: WorkspaceEnv[];
|
||||||
|
isSingleEnvironmentMode?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AzureEntraIdInputForm = ({
|
export const AzureEntraIdInputForm = ({
|
||||||
onCompleted,
|
onCompleted,
|
||||||
onCancel,
|
onCancel,
|
||||||
environment,
|
environments,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectSlug
|
projectSlug,
|
||||||
|
isSingleEnvironmentMode
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -85,7 +89,10 @@ export const AzureEntraIdInputForm = ({
|
|||||||
watch,
|
watch,
|
||||||
handleSubmit
|
handleSubmit
|
||||||
} = useForm<TForm>({
|
} = useForm<TForm>({
|
||||||
resolver: zodResolver(formSchema)
|
resolver: zodResolver(formSchema),
|
||||||
|
defaultValues: {
|
||||||
|
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||||
|
}
|
||||||
});
|
});
|
||||||
const tenantId = watch("provider.tenantId");
|
const tenantId = watch("provider.tenantId");
|
||||||
const applicationId = watch("provider.applicationId");
|
const applicationId = watch("provider.applicationId");
|
||||||
@@ -107,7 +114,8 @@ export const AzureEntraIdInputForm = ({
|
|||||||
selectedUsers,
|
selectedUsers,
|
||||||
provider,
|
provider,
|
||||||
maxTTL,
|
maxTTL,
|
||||||
defaultTTL
|
defaultTTL,
|
||||||
|
environment
|
||||||
}: TForm) => {
|
}: TForm) => {
|
||||||
// wait till previous request is finished
|
// wait till previous request is finished
|
||||||
if (createDynamicSecret.isPending) return;
|
if (createDynamicSecret.isPending) return;
|
||||||
@@ -129,7 +137,7 @@ export const AzureEntraIdInputForm = ({
|
|||||||
path: secretPath,
|
path: secretPath,
|
||||||
defaultTTL,
|
defaultTTL,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environmentSlug: environment
|
environmentSlug: environment.slug
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
onCompleted();
|
onCompleted();
|
||||||
@@ -373,6 +381,29 @@ export const AzureEntraIdInputForm = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{!isSingleEnvironmentMode && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="environment"
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Environment"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<FilterableSelect
|
||||||
|
options={environments}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
placeholder="Select the environment to create secret in..."
|
||||||
|
getOptionLabel={(option) => option.name}
|
||||||
|
getOptionValue={(option) => option.slug}
|
||||||
|
menuPlacement="top"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4 flex items-center space-x-4">
|
<div className="mt-4 flex items-center space-x-4">
|
||||||
<Button type="submit" isLoading={isSubmitting} isDisabled={isLoading || isError}>
|
<Button type="submit" isLoading={isSubmitting} isDisabled={isLoading || isError}>
|
||||||
|
|||||||
+43
-7
@@ -11,6 +11,7 @@ import {
|
|||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionTrigger,
|
AccordionTrigger,
|
||||||
Button,
|
Button,
|
||||||
|
FilterableSelect,
|
||||||
FormControl,
|
FormControl,
|
||||||
Input,
|
Input,
|
||||||
SecretInput,
|
SecretInput,
|
||||||
@@ -18,6 +19,7 @@ import {
|
|||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||||
|
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
provider: z.object({
|
provider: z.object({
|
||||||
@@ -52,7 +54,8 @@ const formSchema = z.object({
|
|||||||
if (valMs > 24 * 60 * 60 * 1000)
|
if (valMs > 24 * 60 * 60 * 1000)
|
||||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||||
}),
|
}),
|
||||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||||
|
environment: z.object({ name: z.string(), slug: z.string() })
|
||||||
});
|
});
|
||||||
type TForm = z.infer<typeof formSchema>;
|
type TForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
@@ -61,7 +64,8 @@ type Props = {
|
|||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
environment: string;
|
environments: WorkspaceEnv[];
|
||||||
|
isSingleEnvironmentMode?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSqlStatements = () => {
|
const getSqlStatements = () => {
|
||||||
@@ -76,9 +80,10 @@ const getSqlStatements = () => {
|
|||||||
export const CassandraInputForm = ({
|
export const CassandraInputForm = ({
|
||||||
onCompleted,
|
onCompleted,
|
||||||
onCancel,
|
onCancel,
|
||||||
environment,
|
environments,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectSlug
|
projectSlug,
|
||||||
|
isSingleEnvironmentMode
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -87,15 +92,23 @@ export const CassandraInputForm = ({
|
|||||||
} = useForm<TForm>({
|
} = useForm<TForm>({
|
||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
provider: getSqlStatements()
|
provider: getSqlStatements(),
|
||||||
|
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const createDynamicSecret = useCreateDynamicSecret();
|
const createDynamicSecret = useCreateDynamicSecret();
|
||||||
|
|
||||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
const handleCreateDynamicSecret = async ({
|
||||||
|
name,
|
||||||
|
maxTTL,
|
||||||
|
provider,
|
||||||
|
defaultTTL,
|
||||||
|
environment
|
||||||
|
}: TForm) => {
|
||||||
// wait till previous request is finished
|
// wait till previous request is finished
|
||||||
if (createDynamicSecret.isPending) return;
|
if (createDynamicSecret.isPending) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await createDynamicSecret.mutateAsync({
|
await createDynamicSecret.mutateAsync({
|
||||||
provider: { type: DynamicSecretProviders.Cassandra, inputs: provider },
|
provider: { type: DynamicSecretProviders.Cassandra, inputs: provider },
|
||||||
@@ -104,7 +117,7 @@ export const CassandraInputForm = ({
|
|||||||
path: secretPath,
|
path: secretPath,
|
||||||
defaultTTL,
|
defaultTTL,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environmentSlug: environment
|
environmentSlug: environment.slug
|
||||||
});
|
});
|
||||||
onCompleted();
|
onCompleted();
|
||||||
} catch {
|
} catch {
|
||||||
@@ -345,6 +358,29 @@ export const CassandraInputForm = ({
|
|||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
{!isSingleEnvironmentMode && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="environment"
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Environment"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<FilterableSelect
|
||||||
|
options={environments}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
placeholder="Select the environment to create secret in..."
|
||||||
|
getOptionLabel={(option) => option.name}
|
||||||
|
getOptionValue={(option) => option.slug}
|
||||||
|
menuPlacement="top"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+36
-18
@@ -17,6 +17,7 @@ import { AnimatePresence, motion } from "framer-motion";
|
|||||||
|
|
||||||
import { Modal, ModalContent } from "@app/components/v2";
|
import { Modal, ModalContent } from "@app/components/v2";
|
||||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||||
|
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||||
|
|
||||||
import { AwsElastiCacheInputForm } from "./AwsElastiCacheInputForm";
|
import { AwsElastiCacheInputForm } from "./AwsElastiCacheInputForm";
|
||||||
import { AwsIamInputForm } from "./AwsIamInputForm";
|
import { AwsIamInputForm } from "./AwsIamInputForm";
|
||||||
@@ -38,8 +39,9 @@ type Props = {
|
|||||||
isOpen?: boolean;
|
isOpen?: boolean;
|
||||||
onToggle: (isOpen: boolean) => void;
|
onToggle: (isOpen: boolean) => void;
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
environment: string;
|
environments: WorkspaceEnv[];
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
|
isSingleEnvironmentMode?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum WizardSteps {
|
enum WizardSteps {
|
||||||
@@ -129,8 +131,9 @@ export const CreateDynamicSecretForm = ({
|
|||||||
isOpen,
|
isOpen,
|
||||||
onToggle,
|
onToggle,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environment,
|
environments,
|
||||||
secretPath
|
secretPath,
|
||||||
|
isSingleEnvironmentMode
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const [wizardStep, setWizardStep] = useState(WizardSteps.SelectProvider);
|
const [wizardStep, setWizardStep] = useState(WizardSteps.SelectProvider);
|
||||||
const [selectedProvider, setSelectedProvider] = useState<DynamicSecretProviders | null>(null);
|
const [selectedProvider, setSelectedProvider] = useState<DynamicSecretProviders | null>(null);
|
||||||
@@ -197,7 +200,8 @@ export const CreateDynamicSecretForm = ({
|
|||||||
onCancel={handleFormReset}
|
onCancel={handleFormReset}
|
||||||
projectSlug={projectSlug}
|
projectSlug={projectSlug}
|
||||||
secretPath={secretPath}
|
secretPath={secretPath}
|
||||||
environment={environment}
|
environments={environments}
|
||||||
|
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
@@ -215,7 +219,8 @@ export const CreateDynamicSecretForm = ({
|
|||||||
onCancel={handleFormReset}
|
onCancel={handleFormReset}
|
||||||
projectSlug={projectSlug}
|
projectSlug={projectSlug}
|
||||||
secretPath={secretPath}
|
secretPath={secretPath}
|
||||||
environment={environment}
|
environments={environments}
|
||||||
|
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
@@ -233,7 +238,8 @@ export const CreateDynamicSecretForm = ({
|
|||||||
onCancel={handleFormReset}
|
onCancel={handleFormReset}
|
||||||
projectSlug={projectSlug}
|
projectSlug={projectSlug}
|
||||||
secretPath={secretPath}
|
secretPath={secretPath}
|
||||||
environment={environment}
|
environments={environments}
|
||||||
|
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
@@ -251,7 +257,8 @@ export const CreateDynamicSecretForm = ({
|
|||||||
onCancel={handleFormReset}
|
onCancel={handleFormReset}
|
||||||
projectSlug={projectSlug}
|
projectSlug={projectSlug}
|
||||||
secretPath={secretPath}
|
secretPath={secretPath}
|
||||||
environment={environment}
|
environments={environments}
|
||||||
|
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
@@ -269,7 +276,8 @@ export const CreateDynamicSecretForm = ({
|
|||||||
onCancel={handleFormReset}
|
onCancel={handleFormReset}
|
||||||
projectSlug={projectSlug}
|
projectSlug={projectSlug}
|
||||||
secretPath={secretPath}
|
secretPath={secretPath}
|
||||||
environment={environment}
|
environments={environments}
|
||||||
|
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
@@ -287,7 +295,8 @@ export const CreateDynamicSecretForm = ({
|
|||||||
onCancel={handleFormReset}
|
onCancel={handleFormReset}
|
||||||
projectSlug={projectSlug}
|
projectSlug={projectSlug}
|
||||||
secretPath={secretPath}
|
secretPath={secretPath}
|
||||||
environment={environment}
|
environments={environments}
|
||||||
|
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
@@ -305,7 +314,8 @@ export const CreateDynamicSecretForm = ({
|
|||||||
onCancel={handleFormReset}
|
onCancel={handleFormReset}
|
||||||
projectSlug={projectSlug}
|
projectSlug={projectSlug}
|
||||||
secretPath={secretPath}
|
secretPath={secretPath}
|
||||||
environment={environment}
|
environments={environments}
|
||||||
|
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
@@ -323,7 +333,8 @@ export const CreateDynamicSecretForm = ({
|
|||||||
onCancel={handleFormReset}
|
onCancel={handleFormReset}
|
||||||
projectSlug={projectSlug}
|
projectSlug={projectSlug}
|
||||||
secretPath={secretPath}
|
secretPath={secretPath}
|
||||||
environment={environment}
|
environments={environments}
|
||||||
|
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
@@ -341,7 +352,8 @@ export const CreateDynamicSecretForm = ({
|
|||||||
onCancel={handleFormReset}
|
onCancel={handleFormReset}
|
||||||
projectSlug={projectSlug}
|
projectSlug={projectSlug}
|
||||||
secretPath={secretPath}
|
secretPath={secretPath}
|
||||||
environment={environment}
|
environments={environments}
|
||||||
|
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
@@ -359,7 +371,8 @@ export const CreateDynamicSecretForm = ({
|
|||||||
onCancel={handleFormReset}
|
onCancel={handleFormReset}
|
||||||
projectSlug={projectSlug}
|
projectSlug={projectSlug}
|
||||||
secretPath={secretPath}
|
secretPath={secretPath}
|
||||||
environment={environment}
|
environments={environments}
|
||||||
|
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
@@ -377,7 +390,8 @@ export const CreateDynamicSecretForm = ({
|
|||||||
onCancel={handleFormReset}
|
onCancel={handleFormReset}
|
||||||
projectSlug={projectSlug}
|
projectSlug={projectSlug}
|
||||||
secretPath={secretPath}
|
secretPath={secretPath}
|
||||||
environment={environment}
|
environments={environments}
|
||||||
|
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
@@ -395,7 +409,8 @@ export const CreateDynamicSecretForm = ({
|
|||||||
onCancel={handleFormReset}
|
onCancel={handleFormReset}
|
||||||
projectSlug={projectSlug}
|
projectSlug={projectSlug}
|
||||||
secretPath={secretPath}
|
secretPath={secretPath}
|
||||||
environment={environment}
|
environments={environments}
|
||||||
|
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
@@ -413,7 +428,8 @@ export const CreateDynamicSecretForm = ({
|
|||||||
onCancel={handleFormReset}
|
onCancel={handleFormReset}
|
||||||
projectSlug={projectSlug}
|
projectSlug={projectSlug}
|
||||||
secretPath={secretPath}
|
secretPath={secretPath}
|
||||||
environment={environment}
|
environments={environments}
|
||||||
|
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
@@ -432,7 +448,8 @@ export const CreateDynamicSecretForm = ({
|
|||||||
onCancel={handleFormReset}
|
onCancel={handleFormReset}
|
||||||
projectSlug={projectSlug}
|
projectSlug={projectSlug}
|
||||||
secretPath={secretPath}
|
secretPath={secretPath}
|
||||||
environment={environment}
|
environments={environments}
|
||||||
|
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
@@ -450,7 +467,8 @@ export const CreateDynamicSecretForm = ({
|
|||||||
onCancel={handleFormReset}
|
onCancel={handleFormReset}
|
||||||
projectSlug={projectSlug}
|
projectSlug={projectSlug}
|
||||||
secretPath={secretPath}
|
secretPath={secretPath}
|
||||||
environment={environment}
|
environments={environments}
|
||||||
|
isSingleEnvironmentMode={isSingleEnvironmentMode}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
+42
-7
@@ -9,6 +9,7 @@ import { TtlFormLabel } from "@app/components/features";
|
|||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
FilterableSelect,
|
||||||
FormControl,
|
FormControl,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
IconButton,
|
IconButton,
|
||||||
@@ -19,6 +20,7 @@ import {
|
|||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||||
|
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||||
|
|
||||||
const authMethods = [
|
const authMethods = [
|
||||||
{
|
{
|
||||||
@@ -73,7 +75,8 @@ const formSchema = z.object({
|
|||||||
if (valMs > 24 * 60 * 60 * 1000)
|
if (valMs > 24 * 60 * 60 * 1000)
|
||||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||||
}),
|
}),
|
||||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||||
|
environment: z.object({ name: z.string(), slug: z.string() })
|
||||||
});
|
});
|
||||||
type TForm = z.infer<typeof formSchema>;
|
type TForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
@@ -82,15 +85,17 @@ type Props = {
|
|||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
environment: string;
|
environments: WorkspaceEnv[];
|
||||||
|
isSingleEnvironmentMode?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ElasticSearchInputForm = ({
|
export const ElasticSearchInputForm = ({
|
||||||
onCompleted,
|
onCompleted,
|
||||||
onCancel,
|
onCancel,
|
||||||
environment,
|
environments,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectSlug
|
projectSlug,
|
||||||
|
isSingleEnvironmentMode
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -107,13 +112,20 @@ export const ElasticSearchInputForm = ({
|
|||||||
},
|
},
|
||||||
roles: ["superuser"],
|
roles: ["superuser"],
|
||||||
port: 443
|
port: 443
|
||||||
}
|
},
|
||||||
|
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const createDynamicSecret = useCreateDynamicSecret();
|
const createDynamicSecret = useCreateDynamicSecret();
|
||||||
|
|
||||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
const handleCreateDynamicSecret = async ({
|
||||||
|
name,
|
||||||
|
maxTTL,
|
||||||
|
provider,
|
||||||
|
defaultTTL,
|
||||||
|
environment
|
||||||
|
}: TForm) => {
|
||||||
// wait till previous request is finished
|
// wait till previous request is finished
|
||||||
if (createDynamicSecret.isPending) return;
|
if (createDynamicSecret.isPending) return;
|
||||||
try {
|
try {
|
||||||
@@ -124,7 +136,7 @@ export const ElasticSearchInputForm = ({
|
|||||||
path: secretPath,
|
path: secretPath,
|
||||||
defaultTTL,
|
defaultTTL,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environmentSlug: environment
|
environmentSlug: environment.slug
|
||||||
});
|
});
|
||||||
onCompleted();
|
onCompleted();
|
||||||
} catch {
|
} catch {
|
||||||
@@ -406,6 +418,29 @@ export const ElasticSearchInputForm = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{!isSingleEnvironmentMode && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="environment"
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Environment"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<FilterableSelect
|
||||||
|
options={environments}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
placeholder="Select the environment to create secret in..."
|
||||||
|
getOptionLabel={(option) => option.name}
|
||||||
|
getOptionValue={(option) => option.slug}
|
||||||
|
menuPlacement="top"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+49
-7
@@ -7,9 +7,18 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { TtlFormLabel } from "@app/components/features";
|
import { TtlFormLabel } from "@app/components/features";
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, Input, Select, SelectItem, TextArea } from "@app/components/v2";
|
import {
|
||||||
|
Button,
|
||||||
|
FilterableSelect,
|
||||||
|
FormControl,
|
||||||
|
Input,
|
||||||
|
Select,
|
||||||
|
SelectItem,
|
||||||
|
TextArea
|
||||||
|
} from "@app/components/v2";
|
||||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||||
|
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||||
|
|
||||||
enum CredentialType {
|
enum CredentialType {
|
||||||
Dynamic = "dynamic",
|
Dynamic = "dynamic",
|
||||||
@@ -69,7 +78,8 @@ const formSchema = z.object({
|
|||||||
if (valMs > 24 * 60 * 60 * 1000)
|
if (valMs > 24 * 60 * 60 * 1000)
|
||||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||||
}),
|
}),
|
||||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||||
|
environment: z.object({ name: z.string(), slug: z.string() })
|
||||||
});
|
});
|
||||||
|
|
||||||
type TForm = z.infer<typeof formSchema>;
|
type TForm = z.infer<typeof formSchema>;
|
||||||
@@ -79,7 +89,8 @@ type Props = {
|
|||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
environment: string;
|
environments: WorkspaceEnv[];
|
||||||
|
isSingleEnvironmentMode?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LdapInputForm = ({
|
export const LdapInputForm = ({
|
||||||
@@ -87,7 +98,8 @@ export const LdapInputForm = ({
|
|||||||
onCancel,
|
onCancel,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environment
|
environments,
|
||||||
|
isSingleEnvironmentMode
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -107,7 +119,8 @@ export const LdapInputForm = ({
|
|||||||
revocationLdif: "",
|
revocationLdif: "",
|
||||||
rollbackLdif: "",
|
rollbackLdif: "",
|
||||||
credentialType: CredentialType.Dynamic
|
credentialType: CredentialType.Dynamic
|
||||||
}
|
},
|
||||||
|
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -115,7 +128,13 @@ export const LdapInputForm = ({
|
|||||||
|
|
||||||
const createDynamicSecret = useCreateDynamicSecret();
|
const createDynamicSecret = useCreateDynamicSecret();
|
||||||
|
|
||||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
const handleCreateDynamicSecret = async ({
|
||||||
|
name,
|
||||||
|
maxTTL,
|
||||||
|
provider,
|
||||||
|
defaultTTL,
|
||||||
|
environment
|
||||||
|
}: TForm) => {
|
||||||
// wait till previous request is finished
|
// wait till previous request is finished
|
||||||
if (createDynamicSecret.isPending) return;
|
if (createDynamicSecret.isPending) return;
|
||||||
try {
|
try {
|
||||||
@@ -126,7 +145,7 @@ export const LdapInputForm = ({
|
|||||||
path: secretPath,
|
path: secretPath,
|
||||||
defaultTTL,
|
defaultTTL,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environmentSlug: environment
|
environmentSlug: environment.slug
|
||||||
});
|
});
|
||||||
onCompleted();
|
onCompleted();
|
||||||
} catch {
|
} catch {
|
||||||
@@ -369,6 +388,29 @@ export const LdapInputForm = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{!isSingleEnvironmentMode && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="environment"
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Environment"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<FilterableSelect
|
||||||
|
options={environments}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
placeholder="Select the environment to create secret in..."
|
||||||
|
getOptionLabel={(option) => option.name}
|
||||||
|
getOptionValue={(option) => option.slug}
|
||||||
|
menuPlacement="top"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+42
-7
@@ -13,6 +13,7 @@ import {
|
|||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionTrigger,
|
AccordionTrigger,
|
||||||
Button,
|
Button,
|
||||||
|
FilterableSelect,
|
||||||
FormControl,
|
FormControl,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
IconButton,
|
IconButton,
|
||||||
@@ -22,6 +23,7 @@ import {
|
|||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||||
|
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
provider: z.object({
|
provider: z.object({
|
||||||
@@ -63,7 +65,8 @@ const formSchema = z.object({
|
|||||||
if (valMs > 24 * 60 * 60 * 1000)
|
if (valMs > 24 * 60 * 60 * 1000)
|
||||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||||
}),
|
}),
|
||||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||||
|
environment: z.object({ name: z.string(), slug: z.string() })
|
||||||
});
|
});
|
||||||
type TForm = z.infer<typeof formSchema>;
|
type TForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
@@ -72,7 +75,8 @@ type Props = {
|
|||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
environment: string;
|
environments: WorkspaceEnv[];
|
||||||
|
isSingleEnvironmentMode?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ATLAS_SCOPE_TYPES = [
|
const ATLAS_SCOPE_TYPES = [
|
||||||
@@ -93,9 +97,10 @@ const ATLAS_SCOPE_TYPES = [
|
|||||||
export const MongoAtlasInputForm = ({
|
export const MongoAtlasInputForm = ({
|
||||||
onCompleted,
|
onCompleted,
|
||||||
onCancel,
|
onCancel,
|
||||||
environment,
|
environments,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectSlug
|
projectSlug,
|
||||||
|
isSingleEnvironmentMode
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -108,7 +113,8 @@ export const MongoAtlasInputForm = ({
|
|||||||
defaultValues: {
|
defaultValues: {
|
||||||
provider: {
|
provider: {
|
||||||
roles: [{ databaseName: "", roleName: "" }]
|
roles: [{ databaseName: "", roleName: "" }]
|
||||||
}
|
},
|
||||||
|
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -124,7 +130,13 @@ export const MongoAtlasInputForm = ({
|
|||||||
|
|
||||||
const createDynamicSecret = useCreateDynamicSecret();
|
const createDynamicSecret = useCreateDynamicSecret();
|
||||||
|
|
||||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
const handleCreateDynamicSecret = async ({
|
||||||
|
name,
|
||||||
|
maxTTL,
|
||||||
|
provider,
|
||||||
|
defaultTTL,
|
||||||
|
environment
|
||||||
|
}: TForm) => {
|
||||||
// wait till previous request is finished
|
// wait till previous request is finished
|
||||||
if (createDynamicSecret.isPending) return;
|
if (createDynamicSecret.isPending) return;
|
||||||
try {
|
try {
|
||||||
@@ -135,7 +147,7 @@ export const MongoAtlasInputForm = ({
|
|||||||
path: secretPath,
|
path: secretPath,
|
||||||
defaultTTL,
|
defaultTTL,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environmentSlug: environment
|
environmentSlug: environment.slug
|
||||||
});
|
});
|
||||||
onCompleted();
|
onCompleted();
|
||||||
} catch {
|
} catch {
|
||||||
@@ -438,6 +450,29 @@ export const MongoAtlasInputForm = ({
|
|||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
{!isSingleEnvironmentMode && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="environment"
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Environment"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<FilterableSelect
|
||||||
|
options={environments}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
placeholder="Select the environment to create secret in..."
|
||||||
|
getOptionLabel={(option) => option.name}
|
||||||
|
getOptionValue={(option) => option.slug}
|
||||||
|
menuPlacement="top"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+50
-8
@@ -7,9 +7,18 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { TtlFormLabel } from "@app/components/features";
|
import { TtlFormLabel } from "@app/components/features";
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, FormLabel, IconButton, Input, SecretInput } from "@app/components/v2";
|
import {
|
||||||
|
Button,
|
||||||
|
FilterableSelect,
|
||||||
|
FormControl,
|
||||||
|
FormLabel,
|
||||||
|
IconButton,
|
||||||
|
Input,
|
||||||
|
SecretInput
|
||||||
|
} from "@app/components/v2";
|
||||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||||
|
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
provider: z.object({
|
provider: z.object({
|
||||||
@@ -46,7 +55,8 @@ const formSchema = z.object({
|
|||||||
if (valMs > 24 * 60 * 60 * 1000)
|
if (valMs > 24 * 60 * 60 * 1000)
|
||||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||||
}),
|
}),
|
||||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||||
|
environment: z.object({ name: z.string(), slug: z.string() })
|
||||||
});
|
});
|
||||||
type TForm = z.infer<typeof formSchema>;
|
type TForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
@@ -55,15 +65,17 @@ type Props = {
|
|||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
environment: string;
|
environments: WorkspaceEnv[];
|
||||||
|
isSingleEnvironmentMode?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MongoDBDatabaseInputForm = ({
|
export const MongoDBDatabaseInputForm = ({
|
||||||
onCompleted,
|
onCompleted,
|
||||||
onCancel,
|
onCancel,
|
||||||
environment,
|
environments,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectSlug
|
projectSlug,
|
||||||
|
isSingleEnvironmentMode
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -76,7 +88,8 @@ export const MongoDBDatabaseInputForm = ({
|
|||||||
defaultValues: {
|
defaultValues: {
|
||||||
provider: {
|
provider: {
|
||||||
roles: [{ roleName: "readWrite" }]
|
roles: [{ roleName: "readWrite" }]
|
||||||
}
|
},
|
||||||
|
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -87,7 +100,13 @@ export const MongoDBDatabaseInputForm = ({
|
|||||||
|
|
||||||
const createDynamicSecret = useCreateDynamicSecret();
|
const createDynamicSecret = useCreateDynamicSecret();
|
||||||
|
|
||||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
const handleCreateDynamicSecret = async ({
|
||||||
|
name,
|
||||||
|
maxTTL,
|
||||||
|
provider,
|
||||||
|
defaultTTL,
|
||||||
|
environment
|
||||||
|
}: TForm) => {
|
||||||
// wait till previous request is finished
|
// wait till previous request is finished
|
||||||
if (createDynamicSecret.isPending) return;
|
if (createDynamicSecret.isPending) return;
|
||||||
try {
|
try {
|
||||||
@@ -105,7 +124,7 @@ export const MongoDBDatabaseInputForm = ({
|
|||||||
path: secretPath,
|
path: secretPath,
|
||||||
defaultTTL,
|
defaultTTL,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environmentSlug: environment
|
environmentSlug: environment.slug
|
||||||
});
|
});
|
||||||
onCompleted();
|
onCompleted();
|
||||||
} catch {
|
} catch {
|
||||||
@@ -321,6 +340,29 @@ export const MongoDBDatabaseInputForm = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{!isSingleEnvironmentMode && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="environment"
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Environment"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<FilterableSelect
|
||||||
|
options={environments}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
placeholder="Select the environment to create secret in..."
|
||||||
|
getOptionLabel={(option) => option.name}
|
||||||
|
getOptionValue={(option) => option.slug}
|
||||||
|
menuPlacement="top"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+50
-8
@@ -7,9 +7,18 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { TtlFormLabel } from "@app/components/features";
|
import { TtlFormLabel } from "@app/components/features";
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, FormLabel, IconButton, Input, SecretInput } from "@app/components/v2";
|
import {
|
||||||
|
Button,
|
||||||
|
FilterableSelect,
|
||||||
|
FormControl,
|
||||||
|
FormLabel,
|
||||||
|
IconButton,
|
||||||
|
Input,
|
||||||
|
SecretInput
|
||||||
|
} from "@app/components/v2";
|
||||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||||
|
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
provider: z.object({
|
provider: z.object({
|
||||||
@@ -50,7 +59,8 @@ const formSchema = z.object({
|
|||||||
if (valMs > 24 * 60 * 60 * 1000)
|
if (valMs > 24 * 60 * 60 * 1000)
|
||||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||||
}),
|
}),
|
||||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||||
|
environment: z.object({ name: z.string(), slug: z.string() })
|
||||||
});
|
});
|
||||||
type TForm = z.infer<typeof formSchema>;
|
type TForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
@@ -59,15 +69,17 @@ type Props = {
|
|||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
environment: string;
|
environments: WorkspaceEnv[];
|
||||||
|
isSingleEnvironmentMode?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RabbitMqInputForm = ({
|
export const RabbitMqInputForm = ({
|
||||||
onCompleted,
|
onCompleted,
|
||||||
onCancel,
|
onCancel,
|
||||||
environment,
|
environments,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectSlug
|
projectSlug,
|
||||||
|
isSingleEnvironmentMode
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -89,13 +101,20 @@ export const RabbitMqInputForm = ({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
tags: []
|
tags: []
|
||||||
}
|
},
|
||||||
|
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const createDynamicSecret = useCreateDynamicSecret();
|
const createDynamicSecret = useCreateDynamicSecret();
|
||||||
|
|
||||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
const handleCreateDynamicSecret = async ({
|
||||||
|
name,
|
||||||
|
maxTTL,
|
||||||
|
provider,
|
||||||
|
defaultTTL,
|
||||||
|
environment
|
||||||
|
}: TForm) => {
|
||||||
// wait till previous request is finished
|
// wait till previous request is finished
|
||||||
if (createDynamicSecret.isPending) return;
|
if (createDynamicSecret.isPending) return;
|
||||||
try {
|
try {
|
||||||
@@ -106,7 +125,7 @@ export const RabbitMqInputForm = ({
|
|||||||
path: secretPath,
|
path: secretPath,
|
||||||
defaultTTL,
|
defaultTTL,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environmentSlug: environment
|
environmentSlug: environment.slug
|
||||||
});
|
});
|
||||||
onCompleted();
|
onCompleted();
|
||||||
} catch {
|
} catch {
|
||||||
@@ -405,6 +424,29 @@ export const RabbitMqInputForm = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{!isSingleEnvironmentMode && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="environment"
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Environment"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<FilterableSelect
|
||||||
|
options={environments}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
placeholder="Select the environment to create secret in..."
|
||||||
|
getOptionLabel={(option) => option.name}
|
||||||
|
getOptionValue={(option) => option.slug}
|
||||||
|
menuPlacement="top"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+42
-7
@@ -11,6 +11,7 @@ import {
|
|||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionTrigger,
|
AccordionTrigger,
|
||||||
Button,
|
Button,
|
||||||
|
FilterableSelect,
|
||||||
FormControl,
|
FormControl,
|
||||||
Input,
|
Input,
|
||||||
SecretInput,
|
SecretInput,
|
||||||
@@ -18,6 +19,7 @@ import {
|
|||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||||
|
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
provider: z.object({
|
provider: z.object({
|
||||||
@@ -50,7 +52,8 @@ const formSchema = z.object({
|
|||||||
if (valMs > 24 * 60 * 60 * 1000)
|
if (valMs > 24 * 60 * 60 * 1000)
|
||||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||||
}),
|
}),
|
||||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||||
|
environment: z.object({ name: z.string(), slug: z.string() })
|
||||||
});
|
});
|
||||||
type TForm = z.infer<typeof formSchema>;
|
type TForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
@@ -59,15 +62,17 @@ type Props = {
|
|||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
environment: string;
|
environments: WorkspaceEnv[];
|
||||||
|
isSingleEnvironmentMode?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RedisInputForm = ({
|
export const RedisInputForm = ({
|
||||||
onCompleted,
|
onCompleted,
|
||||||
onCancel,
|
onCancel,
|
||||||
environment,
|
environments,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectSlug
|
projectSlug,
|
||||||
|
isSingleEnvironmentMode
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -81,13 +86,20 @@ export const RedisInputForm = ({
|
|||||||
port: 6379,
|
port: 6379,
|
||||||
creationStatement: "ACL SETUSER {{username}} on >{{password}} ~* &* +@all",
|
creationStatement: "ACL SETUSER {{username}} on >{{password}} ~* &* +@all",
|
||||||
revocationStatement: "ACL DELUSER {{username}}"
|
revocationStatement: "ACL DELUSER {{username}}"
|
||||||
}
|
},
|
||||||
|
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const createDynamicSecret = useCreateDynamicSecret();
|
const createDynamicSecret = useCreateDynamicSecret();
|
||||||
|
|
||||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
const handleCreateDynamicSecret = async ({
|
||||||
|
name,
|
||||||
|
maxTTL,
|
||||||
|
provider,
|
||||||
|
defaultTTL,
|
||||||
|
environment
|
||||||
|
}: TForm) => {
|
||||||
// wait till previous request is finished
|
// wait till previous request is finished
|
||||||
if (createDynamicSecret.isPending) return;
|
if (createDynamicSecret.isPending) return;
|
||||||
try {
|
try {
|
||||||
@@ -98,7 +110,7 @@ export const RedisInputForm = ({
|
|||||||
path: secretPath,
|
path: secretPath,
|
||||||
defaultTTL,
|
defaultTTL,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environmentSlug: environment
|
environmentSlug: environment.slug
|
||||||
});
|
});
|
||||||
onCompleted();
|
onCompleted();
|
||||||
} catch {
|
} catch {
|
||||||
@@ -313,6 +325,29 @@ export const RedisInputForm = ({
|
|||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
{!isSingleEnvironmentMode && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="environment"
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Environment"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<FilterableSelect
|
||||||
|
options={environments}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
placeholder="Select the environment to create secret in..."
|
||||||
|
getOptionLabel={(option) => option.name}
|
||||||
|
getOptionValue={(option) => option.slug}
|
||||||
|
menuPlacement="top"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+42
-7
@@ -11,12 +11,14 @@ import {
|
|||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionTrigger,
|
AccordionTrigger,
|
||||||
Button,
|
Button,
|
||||||
|
FilterableSelect,
|
||||||
FormControl,
|
FormControl,
|
||||||
Input,
|
Input,
|
||||||
TextArea
|
TextArea
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||||
|
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
provider: z.object({
|
provider: z.object({
|
||||||
@@ -48,7 +50,8 @@ const formSchema = z.object({
|
|||||||
if (valMs > 24 * 60 * 60 * 1000)
|
if (valMs > 24 * 60 * 60 * 1000)
|
||||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||||
}),
|
}),
|
||||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||||
|
environment: z.object({ name: z.string(), slug: z.string() })
|
||||||
});
|
});
|
||||||
type TForm = z.infer<typeof formSchema>;
|
type TForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
@@ -57,15 +60,17 @@ type Props = {
|
|||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
environment: string;
|
environments: WorkspaceEnv[];
|
||||||
|
isSingleEnvironmentMode?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SapAseInputForm = ({
|
export const SapAseInputForm = ({
|
||||||
onCompleted,
|
onCompleted,
|
||||||
onCancel,
|
onCancel,
|
||||||
environment,
|
environments,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectSlug
|
projectSlug,
|
||||||
|
isSingleEnvironmentMode
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -82,13 +87,20 @@ sp_adduser '{{username}}', '{{username}}', null;
|
|||||||
sp_role 'grant', 'mon_role', '{{username}}';`,
|
sp_role 'grant', 'mon_role', '{{username}}';`,
|
||||||
revocationStatement: `sp_dropuser '{{username}}';
|
revocationStatement: `sp_dropuser '{{username}}';
|
||||||
sp_droplogin '{{username}}';`
|
sp_droplogin '{{username}}';`
|
||||||
}
|
},
|
||||||
|
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const createDynamicSecret = useCreateDynamicSecret();
|
const createDynamicSecret = useCreateDynamicSecret();
|
||||||
|
|
||||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
const handleCreateDynamicSecret = async ({
|
||||||
|
name,
|
||||||
|
maxTTL,
|
||||||
|
provider,
|
||||||
|
defaultTTL,
|
||||||
|
environment
|
||||||
|
}: TForm) => {
|
||||||
// wait till previous request is finished
|
// wait till previous request is finished
|
||||||
if (createDynamicSecret.isPending) return;
|
if (createDynamicSecret.isPending) return;
|
||||||
try {
|
try {
|
||||||
@@ -99,7 +111,7 @@ sp_droplogin '{{username}}';`
|
|||||||
path: secretPath,
|
path: secretPath,
|
||||||
defaultTTL,
|
defaultTTL,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environmentSlug: environment
|
environmentSlug: environment.slug
|
||||||
});
|
});
|
||||||
onCompleted();
|
onCompleted();
|
||||||
} catch {
|
} catch {
|
||||||
@@ -291,6 +303,29 @@ sp_droplogin '{{username}}';`
|
|||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
{!isSingleEnvironmentMode && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="environment"
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Environment"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<FilterableSelect
|
||||||
|
options={environments}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
placeholder="Select the environment to create secret in..."
|
||||||
|
getOptionLabel={(option) => option.name}
|
||||||
|
getOptionValue={(option) => option.slug}
|
||||||
|
menuPlacement="top"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+42
-7
@@ -11,6 +11,7 @@ import {
|
|||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionTrigger,
|
AccordionTrigger,
|
||||||
Button,
|
Button,
|
||||||
|
FilterableSelect,
|
||||||
FormControl,
|
FormControl,
|
||||||
Input,
|
Input,
|
||||||
SecretInput,
|
SecretInput,
|
||||||
@@ -18,6 +19,7 @@ import {
|
|||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||||
|
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
provider: z.object({
|
provider: z.object({
|
||||||
@@ -50,7 +52,8 @@ const formSchema = z.object({
|
|||||||
if (valMs > 24 * 60 * 60 * 1000)
|
if (valMs > 24 * 60 * 60 * 1000)
|
||||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||||
}),
|
}),
|
||||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||||
|
environment: z.object({ name: z.string(), slug: z.string() })
|
||||||
});
|
});
|
||||||
type TForm = z.infer<typeof formSchema>;
|
type TForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
@@ -59,15 +62,17 @@ type Props = {
|
|||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
environment: string;
|
environments: WorkspaceEnv[];
|
||||||
|
isSingleEnvironmentMode?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SapHanaInputForm = ({
|
export const SapHanaInputForm = ({
|
||||||
onCompleted,
|
onCompleted,
|
||||||
onCancel,
|
onCancel,
|
||||||
environment,
|
environments,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectSlug
|
projectSlug,
|
||||||
|
isSingleEnvironmentMode
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -82,13 +87,20 @@ GRANT "MONITORING" TO {{username}};`,
|
|||||||
revocationStatement: `REVOKE "MONITORING" FROM {{username}};
|
revocationStatement: `REVOKE "MONITORING" FROM {{username}};
|
||||||
DROP USER {{username}};`,
|
DROP USER {{username}};`,
|
||||||
renewStatement: "ALTER USER {{username}} VALID UNTIL '{{expiration}}';"
|
renewStatement: "ALTER USER {{username}} VALID UNTIL '{{expiration}}';"
|
||||||
}
|
},
|
||||||
|
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const createDynamicSecret = useCreateDynamicSecret();
|
const createDynamicSecret = useCreateDynamicSecret();
|
||||||
|
|
||||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
const handleCreateDynamicSecret = async ({
|
||||||
|
name,
|
||||||
|
maxTTL,
|
||||||
|
provider,
|
||||||
|
defaultTTL,
|
||||||
|
environment
|
||||||
|
}: TForm) => {
|
||||||
// wait till previous request is finished
|
// wait till previous request is finished
|
||||||
if (createDynamicSecret.isPending) return;
|
if (createDynamicSecret.isPending) return;
|
||||||
try {
|
try {
|
||||||
@@ -99,7 +111,7 @@ DROP USER {{username}};`,
|
|||||||
path: secretPath,
|
path: secretPath,
|
||||||
defaultTTL,
|
defaultTTL,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environmentSlug: environment
|
environmentSlug: environment.slug
|
||||||
});
|
});
|
||||||
onCompleted();
|
onCompleted();
|
||||||
} catch {
|
} catch {
|
||||||
@@ -311,6 +323,29 @@ DROP USER {{username}};`,
|
|||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
{!isSingleEnvironmentMode && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="environment"
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Environment"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<FilterableSelect
|
||||||
|
options={environments}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
placeholder="Select the environment to create secret in..."
|
||||||
|
getOptionLabel={(option) => option.name}
|
||||||
|
getOptionValue={(option) => option.slug}
|
||||||
|
menuPlacement="top"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+42
-7
@@ -13,12 +13,14 @@ import {
|
|||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionTrigger,
|
AccordionTrigger,
|
||||||
Button,
|
Button,
|
||||||
|
FilterableSelect,
|
||||||
FormControl,
|
FormControl,
|
||||||
Input,
|
Input,
|
||||||
TextArea
|
TextArea
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||||
|
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
provider: z.object({
|
provider: z.object({
|
||||||
@@ -54,7 +56,8 @@ const formSchema = z.object({
|
|||||||
.string()
|
.string()
|
||||||
.trim()
|
.trim()
|
||||||
.min(1)
|
.min(1)
|
||||||
.refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
.refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||||
|
environment: z.object({ name: z.string(), slug: z.string() })
|
||||||
});
|
});
|
||||||
type TForm = z.infer<typeof formSchema>;
|
type TForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
@@ -63,15 +66,17 @@ type Props = {
|
|||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
environment: string;
|
environments: WorkspaceEnv[];
|
||||||
|
isSingleEnvironmentMode?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SnowflakeInputForm = ({
|
export const SnowflakeInputForm = ({
|
||||||
onCompleted,
|
onCompleted,
|
||||||
onCancel,
|
onCancel,
|
||||||
environment,
|
environments,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectSlug
|
projectSlug,
|
||||||
|
isSingleEnvironmentMode
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -85,13 +90,20 @@ export const SnowflakeInputForm = ({
|
|||||||
"CREATE USER {{username}} PASSWORD = '{{password}}' DEFAULT_ROLE = public DEFAULT_SECONDARY_ROLES = ('ALL') MUST_CHANGE_PASSWORD = FALSE DAYS_TO_EXPIRY = {{expiration}};",
|
"CREATE USER {{username}} PASSWORD = '{{password}}' DEFAULT_ROLE = public DEFAULT_SECONDARY_ROLES = ('ALL') MUST_CHANGE_PASSWORD = FALSE DAYS_TO_EXPIRY = {{expiration}};",
|
||||||
revocationStatement: "DROP USER {{username}};",
|
revocationStatement: "DROP USER {{username}};",
|
||||||
renewStatement: "ALTER USER {{username}} SET DAYS_TO_EXPIRY = {{expiration}};"
|
renewStatement: "ALTER USER {{username}} SET DAYS_TO_EXPIRY = {{expiration}};"
|
||||||
}
|
},
|
||||||
|
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const createDynamicSecret = useCreateDynamicSecret();
|
const createDynamicSecret = useCreateDynamicSecret();
|
||||||
|
|
||||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
const handleCreateDynamicSecret = async ({
|
||||||
|
name,
|
||||||
|
maxTTL,
|
||||||
|
provider,
|
||||||
|
defaultTTL,
|
||||||
|
environment
|
||||||
|
}: TForm) => {
|
||||||
// wait till previous request is finished
|
// wait till previous request is finished
|
||||||
if (createDynamicSecret.isPending) return;
|
if (createDynamicSecret.isPending) return;
|
||||||
try {
|
try {
|
||||||
@@ -102,7 +114,7 @@ export const SnowflakeInputForm = ({
|
|||||||
path: secretPath,
|
path: secretPath,
|
||||||
defaultTTL,
|
defaultTTL,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environmentSlug: environment
|
environmentSlug: environment.slug
|
||||||
});
|
});
|
||||||
onCompleted();
|
onCompleted();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -314,6 +326,29 @@ export const SnowflakeInputForm = ({
|
|||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
{!isSingleEnvironmentMode && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="environment"
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Environment"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<FilterableSelect
|
||||||
|
options={environments}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
placeholder="Select the environment to create secret in..."
|
||||||
|
getOptionLabel={(option) => option.name}
|
||||||
|
getOptionValue={(option) => option.slug}
|
||||||
|
menuPlacement="top"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+43
-7
@@ -12,6 +12,7 @@ import {
|
|||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionTrigger,
|
AccordionTrigger,
|
||||||
Button,
|
Button,
|
||||||
|
FilterableSelect,
|
||||||
FormControl,
|
FormControl,
|
||||||
Input,
|
Input,
|
||||||
SecretInput,
|
SecretInput,
|
||||||
@@ -22,6 +23,7 @@ import {
|
|||||||
import { useWorkspace } from "@app/context";
|
import { useWorkspace } from "@app/context";
|
||||||
import { gatewaysQueryKeys, useCreateDynamicSecret } from "@app/hooks/api";
|
import { gatewaysQueryKeys, useCreateDynamicSecret } from "@app/hooks/api";
|
||||||
import { DynamicSecretProviders, SqlProviders } from "@app/hooks/api/dynamicSecret/types";
|
import { DynamicSecretProviders, SqlProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||||
|
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||||
|
|
||||||
const passwordRequirementsSchema = z
|
const passwordRequirementsSchema = z
|
||||||
.object({
|
.object({
|
||||||
@@ -79,7 +81,8 @@ const formSchema = z.object({
|
|||||||
if (valMs > 24 * 60 * 60 * 1000)
|
if (valMs > 24 * 60 * 60 * 1000)
|
||||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||||
}),
|
}),
|
||||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||||
|
environment: z.object({ name: z.string(), slug: z.string() })
|
||||||
});
|
});
|
||||||
type TForm = z.infer<typeof formSchema>;
|
type TForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
@@ -88,7 +91,8 @@ type Props = {
|
|||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
environment: string;
|
environments: WorkspaceEnv[];
|
||||||
|
isSingleEnvironmentMode?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSqlStatements = (provider: SqlProviders) => {
|
const getSqlStatements = (provider: SqlProviders) => {
|
||||||
@@ -145,9 +149,10 @@ const getDefaultPort = (provider: SqlProviders) => {
|
|||||||
export const SqlDatabaseInputForm = ({
|
export const SqlDatabaseInputForm = ({
|
||||||
onCompleted,
|
onCompleted,
|
||||||
onCancel,
|
onCancel,
|
||||||
environment,
|
environments,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectSlug
|
projectSlug,
|
||||||
|
isSingleEnvironmentMode
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|
||||||
@@ -172,7 +177,8 @@ export const SqlDatabaseInputForm = ({
|
|||||||
},
|
},
|
||||||
allowedSymbols: "-_.~!*"
|
allowedSymbols: "-_.~!*"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -181,9 +187,16 @@ export const SqlDatabaseInputForm = ({
|
|||||||
gatewaysQueryKeys.listProjectGateways({ projectId: currentWorkspace.id })
|
gatewaysQueryKeys.listProjectGateways({ projectId: currentWorkspace.id })
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
const handleCreateDynamicSecret = async ({
|
||||||
|
name,
|
||||||
|
maxTTL,
|
||||||
|
provider,
|
||||||
|
defaultTTL,
|
||||||
|
environment
|
||||||
|
}: TForm) => {
|
||||||
// wait till previous request is finished
|
// wait till previous request is finished
|
||||||
if (createDynamicSecret.isPending) return;
|
if (createDynamicSecret.isPending) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await createDynamicSecret.mutateAsync({
|
await createDynamicSecret.mutateAsync({
|
||||||
provider: { type: DynamicSecretProviders.SqlDatabase, inputs: provider },
|
provider: { type: DynamicSecretProviders.SqlDatabase, inputs: provider },
|
||||||
@@ -192,7 +205,7 @@ export const SqlDatabaseInputForm = ({
|
|||||||
path: secretPath,
|
path: secretPath,
|
||||||
defaultTTL,
|
defaultTTL,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environmentSlug: environment
|
environmentSlug: environment.slug
|
||||||
});
|
});
|
||||||
onCompleted();
|
onCompleted();
|
||||||
} catch {
|
} catch {
|
||||||
@@ -649,6 +662,29 @@ export const SqlDatabaseInputForm = ({
|
|||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
{!isSingleEnvironmentMode && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="environment"
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Environment"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<FilterableSelect
|
||||||
|
options={environments}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
placeholder="Select the environment to create secret in..."
|
||||||
|
getOptionLabel={(option) => option.name}
|
||||||
|
getOptionValue={(option) => option.slug}
|
||||||
|
menuPlacement="top"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+43
-8
@@ -5,9 +5,17 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, Input, Select, SelectItem } from "@app/components/v2";
|
import {
|
||||||
|
Button,
|
||||||
|
FilterableSelect,
|
||||||
|
FormControl,
|
||||||
|
Input,
|
||||||
|
Select,
|
||||||
|
SelectItem
|
||||||
|
} from "@app/components/v2";
|
||||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||||
|
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||||
|
|
||||||
enum ConfigType {
|
enum ConfigType {
|
||||||
URL = "url",
|
URL = "url",
|
||||||
@@ -52,7 +60,8 @@ const formSchema = z.object({
|
|||||||
.string()
|
.string()
|
||||||
.trim()
|
.trim()
|
||||||
.min(1)
|
.min(1)
|
||||||
.refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
.refine((val) => val.toLowerCase() === val, "Must be lowercase"),
|
||||||
|
environment: z.object({ name: z.string(), slug: z.string() })
|
||||||
});
|
});
|
||||||
|
|
||||||
type TForm = z.infer<typeof formSchema>;
|
type TForm = z.infer<typeof formSchema>;
|
||||||
@@ -62,15 +71,17 @@ type Props = {
|
|||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
environment: string;
|
environments: WorkspaceEnv[];
|
||||||
|
isSingleEnvironmentMode?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TotpInputForm = ({
|
export const TotpInputForm = ({
|
||||||
onCompleted,
|
onCompleted,
|
||||||
onCancel,
|
onCancel,
|
||||||
environment,
|
environments,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectSlug
|
projectSlug,
|
||||||
|
isSingleEnvironmentMode
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -82,7 +93,8 @@ export const TotpInputForm = ({
|
|||||||
defaultValues: {
|
defaultValues: {
|
||||||
provider: {
|
provider: {
|
||||||
configType: ConfigType.URL
|
configType: ConfigType.URL
|
||||||
}
|
},
|
||||||
|
environment: isSingleEnvironmentMode ? environments[0] : undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -90,7 +102,7 @@ export const TotpInputForm = ({
|
|||||||
|
|
||||||
const createDynamicSecret = useCreateDynamicSecret();
|
const createDynamicSecret = useCreateDynamicSecret();
|
||||||
|
|
||||||
const handleCreateDynamicSecret = async ({ name, provider }: TForm) => {
|
const handleCreateDynamicSecret = async ({ name, provider, environment }: TForm) => {
|
||||||
// wait till previous request is finished
|
// wait till previous request is finished
|
||||||
if (createDynamicSecret.isPending) return;
|
if (createDynamicSecret.isPending) return;
|
||||||
try {
|
try {
|
||||||
@@ -101,7 +113,7 @@ export const TotpInputForm = ({
|
|||||||
path: secretPath,
|
path: secretPath,
|
||||||
defaultTTL: "1m",
|
defaultTTL: "1m",
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environmentSlug: environment
|
environmentSlug: environment.slug
|
||||||
});
|
});
|
||||||
onCompleted();
|
onCompleted();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -295,6 +307,29 @@ export const TotpInputForm = ({
|
|||||||
</p>
|
</p>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{!isSingleEnvironmentMode && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="environment"
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Environment"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<FilterableSelect
|
||||||
|
options={environments}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
placeholder="Select the environment to create secret in..."
|
||||||
|
getOptionLabel={(option) => option.name}
|
||||||
|
getOptionValue={(option) => option.slug}
|
||||||
|
menuPlacement="top"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user