feat: switched to projecet gateway, updated icon, cli

This commit is contained in:
=
2025-02-26 20:45:38 +05:30
parent 929a41065c
commit 917573931f
28 changed files with 345 additions and 115 deletions
+4 -4
View File
@@ -12,7 +12,7 @@ export const useDeleteGatewayById = () => {
return apiRequest.delete(`/api/v1/gateways/${id}`);
},
onSuccess: () => {
queryClient.invalidateQueries(gatewaysQueryKeys.list({}));
queryClient.invalidateQueries(gatewaysQueryKeys.list());
}
});
};
@@ -20,11 +20,11 @@ export const useDeleteGatewayById = () => {
export const useUpdateGatewayById = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, name }: TUpdateGatewayDTO) => {
return apiRequest.patch(`/api/v1/gateways/${id}`, { name });
mutationFn: ({ id, name, projectIds }: TUpdateGatewayDTO) => {
return apiRequest.patch(`/api/v1/gateways/${id}`, { name, projectIds });
},
onSuccess: () => {
queryClient.invalidateQueries(gatewaysQueryKeys.list({}));
queryClient.invalidateQueries(gatewaysQueryKeys.list());
}
});
};
+16 -7
View File
@@ -2,22 +2,31 @@ import { queryOptions } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { TGateway, TListGatewayDTO } from "./types";
import { TGateway, TListProjectGatewayDTO, TProjectGateway } from "./types";
export const gatewaysQueryKeys = {
allKey: () => ["gateways"],
listKey: ({ projectId }: TListGatewayDTO) => [
listKey: () => [...gatewaysQueryKeys.allKey(), "list"],
list: () =>
queryOptions({
queryKey: gatewaysQueryKeys.listKey(),
queryFn: async () => {
const { data } = await apiRequest.get<{ gateways: TGateway[] }>("/api/v1/gateways");
return data.gateways;
}
}),
listProjectGatewayKey: ({ projectId }: TListProjectGatewayDTO) => [
...gatewaysQueryKeys.allKey(),
"list",
{ projectId }
],
list: ({ projectId }: TListGatewayDTO = {}) =>
listProjectGateways: ({ projectId }: TListProjectGatewayDTO) =>
queryOptions({
queryKey: gatewaysQueryKeys.listKey({ projectId }),
queryKey: gatewaysQueryKeys.listProjectGatewayKey({ projectId }),
queryFn: async () => {
const { data } = await apiRequest.get<{ gateways: TGateway[] }>("/api/v1/gateways", {
params: { projectId }
});
const { data } = await apiRequest.get<{ gateways: TProjectGateway[] }>(
`/api/v1/gateways/projects/${projectId}`
);
return data.gateways;
}
})
+24 -2
View File
@@ -11,17 +11,39 @@ export type TGateway = {
name: string;
id: string;
};
projects: {
name: string;
id: string;
slug: string;
}[];
};
export type TProjectGateway = {
id: string;
identityId: string;
name: string;
createdAt: string;
updatedAt: string;
issuedAt: string;
serialNumber: string;
heartbeat: string;
projectGatewayId: string;
identity: {
name: string;
id: string;
};
};
export type TUpdateGatewayDTO = {
id: string;
name?: string;
projectIds?: string[];
};
export type TDeleteGatewayDTO = {
id: string;
};
export type TListGatewayDTO = {
projectId?: string;
export type TListProjectGatewayDTO = {
projectId: string;
};
@@ -103,7 +103,7 @@ export const OrganizationLayout = () => {
</Link>
<Link to="/organization/gateways">
{({ isActive }) => (
<MenuItem isSelected={isActive} icon="jigsaw-puzzle">
<MenuItem isSelected={isActive} icon="gateway" iconMode="reverse">
Gateways
</MenuItem>
)}
@@ -32,6 +32,7 @@ import {
Table,
TableContainer,
TableSkeleton,
Tag,
TBody,
Td,
Th,
@@ -127,6 +128,7 @@ export const GatewayListPage = withPermission(
<Tr>
<Th className="w-1/3">Name</Th>
<Th>Cert Issued At</Th>
<Th>Projects</Th>
<Th>Identity</Th>
<Th>
Health Check
@@ -149,6 +151,13 @@ export const GatewayListPage = withPermission(
<Tr key={el.id}>
<Td>{el.name}</Td>
<Td>{format(new Date(el.issuedAt), "yyyy-MM-dd hh:mm:ss aaa")}</Td>
<Td>
{el.projects.map((projectDetails) => (
<Tag key={projectDetails.id} size="xs">
{projectDetails.name}
</Tag>
))}
</Td>
<Td>{el.identity.name}</Td>
<Td>
{el.heartbeat
@@ -3,9 +3,10 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { createNotification } from "@app/components/notifications";
import { Button, FormControl, Input } from "@app/components/v2";
import { useUpdateGatewayById } from "@app/hooks/api";
import { Button, FilterableSelect, FormControl, Input } from "@app/components/v2";
import { useGetUserWorkspaces, useUpdateGatewayById } from "@app/hooks/api";
import { TGateway } from "@app/hooks/api/gateways/types";
import { ProjectType } from "@app/hooks/api/workspace/types";
type Props = {
gatewayDetails: TGateway;
@@ -13,7 +14,13 @@ type Props = {
};
const schema = z.object({
name: z.string()
name: z.string(),
projects: z
.object({
id: z.string(),
name: z.string()
})
.array()
});
export type FormData = z.infer<typeof schema>;
@@ -31,13 +38,20 @@ export const EditGatewayDetailsModal = ({ gatewayDetails, onClose }: Props) => {
});
const updateGatewayById = useUpdateGatewayById();
// when gateway goes to other products switch to all
const { data: secretManagerWorkspaces, isLoading: isSecretManagerLoading } = useGetUserWorkspaces(
{
type: ProjectType.SecretManager
}
);
const onFormSubmit = ({ name }: FormData) => {
const onFormSubmit = ({ name, projects }: FormData) => {
if (isSubmitting) return;
updateGatewayById.mutate(
{
id: gatewayDetails.id,
name
name,
projectIds: projects.map((el) => el.id)
},
{
onSuccess: () => {
@@ -62,6 +76,30 @@ export const EditGatewayDetailsModal = ({ gatewayDetails, onClose }: Props) => {
</FormControl>
)}
/>
<Controller
control={control}
name="projects"
render={({ field: { onChange, value }, fieldState: { error } }) => (
<FormControl
className="w-full"
label="Projects"
tooltipText="Select the project that you wish to assign to the gateway."
errorText={error?.message}
isError={Boolean(error)}
>
<FilterableSelect
options={secretManagerWorkspaces}
placeholder="Select projects..."
value={value}
onChange={onChange}
isMulti
isLoading={isSecretManagerLoading}
getOptionValue={(option) => option.id}
getOptionLabel={(option) => option.name}
/>
</FormControl>
)}
/>
<div className="mt-4 flex items-center">
<Button className="mr-4" size="sm" type="submit" isLoading={isSubmitting}>
Update
@@ -74,7 +74,7 @@ type Props = {
title: string;
formName: keyof Omit<
Exclude<TFormSchema["permissions"], undefined>,
"workspace" | "organization-admin-console" | "kmip"
"workspace" | "organization-admin-console" | "kmip" | "gateway"
>;
setValue: UseFormSetValue<TFormSchema>;
control: Control<TFormSchema>;
@@ -143,7 +143,7 @@ export const SqlDatabaseInputForm = ({
const createDynamicSecret = useCreateDynamicSecret();
const { data: projectGateways, isPending: isProjectGatewaysLoading } = useQuery(
gatewaysQueryKeys.list({ projectId: currentWorkspace.id })
gatewaysQueryKeys.listProjectGateways({ projectId: currentWorkspace.id })
);
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
@@ -36,7 +36,7 @@ const formSchema = z.object({
revocationStatement: z.string().min(1),
renewStatement: z.string().optional(),
ca: z.string().optional(),
gatewayId: z.string().optional()
projectGatewayId: z.string().optional()
})
.partial(),
defaultTTL: z.string().superRefine((val, ctx) => {
@@ -100,12 +100,13 @@ export const EditDynamicSecretSqlProviderForm = ({
});
const { currentWorkspace } = useWorkspace();
const { data: projectGateways, isPending: isProjectGatewaysLoading } = useQuery(
gatewaysQueryKeys.list({ projectId: currentWorkspace.id })
gatewaysQueryKeys.listProjectGateways({ projectId: currentWorkspace.id })
);
const updateDynamicSecret = useUpdateDynamicSecret();
const selectedGatewayId = watch("inputs.gatewayId");
const isGatewayInActive = projectGateways?.findIndex((el) => el.id === selectedGatewayId) === -1;
const selectedProjectGatewayId = watch("inputs.projectGatewayId");
const isGatewayInActive =
projectGateways?.findIndex((el) => el.projectGatewayId === selectedProjectGatewayId) === -1;
const handleUpdateDynamicSecret = async ({ inputs, maxTTL, defaultTTL, newName }: TForm) => {
// wait till previous request is finished
@@ -119,7 +120,10 @@ export const EditDynamicSecretSqlProviderForm = ({
data: {
maxTTL: maxTTL || undefined,
defaultTTL,
inputs: { ...inputs, gatewayId: isGatewayInActive ? null : inputs.gatewayId },
inputs: {
...inputs,
projectGatewayId: isGatewayInActive ? null : inputs.projectGatewayId
},
newName: newName === dynamicSecret.name ? undefined : newName
}
});
@@ -189,14 +193,14 @@ export const EditDynamicSecretSqlProviderForm = ({
<div>
<Controller
control={control}
name="inputs.gatewayId"
name="inputs.projectGatewayId"
defaultValue=""
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
isError={Boolean(error?.message) || isGatewayInActive}
errorText={
isGatewayInActive && selectedGatewayId
? `Gateway ${selectedGatewayId} is removed`
isGatewayInActive && selectedProjectGatewayId
? `Project Gateway ${selectedProjectGatewayId} is removed`
: error?.message
}
label="Gateway"
@@ -215,7 +219,7 @@ export const EditDynamicSecretSqlProviderForm = ({
Internet Gateway
</SelectItem>
{projectGateways?.map((el) => (
<SelectItem value={el.id} key={el.id}>
<SelectItem value={el.projectGatewayId} key={el.id}>
{el.name}
</SelectItem>
))}