Merge pull request #4462 from Infisical/feat/gateway-v2

feat: gateway v2
This commit is contained in:
Sheen
2025-09-11 04:25:52 +08:00
committed by GitHub
70 changed files with 4963 additions and 378 deletions

View File

@@ -0,0 +1 @@
export * from "./mutations";

View File

@@ -0,0 +1,17 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { gatewaysQueryKeys } from "../gateways/queries";
export const useDeleteGatewayV2ById = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (id: string) => {
return apiRequest.delete(`/api/v2/gateways/${id}`);
},
onSuccess: () => {
queryClient.invalidateQueries(gatewaysQueryKeys.list());
}
});
};

View File

@@ -0,0 +1,12 @@
export type TGatewayV2 = {
id: string;
identityId: string;
name: string;
createdAt: string;
updatedAt: string;
heartbeat: string;
identity: {
name: string;
id: string;
};
};

View File

@@ -2,6 +2,7 @@ import { queryOptions } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { TGatewayV2 } from "../gateways-v2/types";
import { TGateway } from "./types";
export const gatewaysQueryKeys = {
@@ -11,8 +12,21 @@ export const gatewaysQueryKeys = {
queryOptions({
queryKey: gatewaysQueryKeys.listKey(),
queryFn: async () => {
const { data } = await apiRequest.get<{ gateways: TGateway[] }>("/api/v1/gateways");
return data.gateways;
const [{ data }, { data: dataV2 }] = await Promise.all([
apiRequest.get<{ gateways: TGateway[] }>("/api/v1/gateways"),
apiRequest.get<TGatewayV2[]>("/api/v2/gateways")
]);
return [
...data.gateways.map((g) => ({
...g,
isV1: true
})),
...dataV2.map((g) => ({
...g,
isV1: false
}))
];
}
})
};

View File

@@ -14,7 +14,7 @@ import {
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useQuery } from "@tanstack/react-query";
import { format, formatRelative } from "date-fns";
import { formatRelative } from "date-fns";
import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions";
@@ -48,13 +48,14 @@ import {
import { withPermission } from "@app/hoc";
import { usePopUp } from "@app/hooks";
import { gatewaysQueryKeys, useDeleteGatewayById } from "@app/hooks/api/gateways";
import { useDeleteGatewayV2ById } from "@app/hooks/api/gateways-v2";
import { EditGatewayDetailsModal } from "./components/EditGatewayDetailsModal";
export const GatewayListPage = withPermission(
() => {
const [search, setSearch] = useState("");
const { data: gateways, isPending: isGatewayLoading } = useQuery(gatewaysQueryKeys.list());
const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list());
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([
"deleteGateway",
@@ -62,16 +63,20 @@ export const GatewayListPage = withPermission(
] as const);
const deleteGatewayById = useDeleteGatewayById();
const deleteGatewayV2ById = useDeleteGatewayV2ById();
const handleDeleteGateway = async () => {
await deleteGatewayById.mutateAsync((popUp.deleteGateway.data as { id: string }).id, {
onSuccess: () => {
handlePopUpToggle("deleteGateway");
createNotification({
type: "success",
text: "Successfully delete gateway"
});
}
const data = popUp.deleteGateway.data as { id: string; isV1: boolean };
if (data.isV1) {
await deleteGatewayById.mutateAsync(data.id);
} else {
await deleteGatewayV2ById.mutateAsync(data.id);
}
handlePopUpToggle("deleteGateway");
createNotification({
type: "success",
text: "Successfully deleted gateway"
});
};
@@ -127,7 +132,6 @@ export const GatewayListPage = withPermission(
<THead>
<Tr>
<Th className="w-1/3">Name</Th>
<Th>Cert Issued At</Th>
<Th>Identity</Th>
<Th>
Health Check
@@ -143,13 +147,19 @@ export const GatewayListPage = withPermission(
</Tr>
</THead>
<TBody>
{isGatewayLoading && (
{isGatewaysLoading && (
<TableSkeleton innerKey="gateway-table" columns={4} key="gateway-table" />
)}
{filteredGateway?.map((el) => (
<Tr key={el.id}>
<Td>{el.name}</Td>
<Td>{format(new Date(el.issuedAt), "yyyy-MM-dd hh:mm:ss aaa")}</Td>
<Td>
<div className="flex items-center gap-2">
<span>{el.name}</span>
<span className="rounded bg-mineshaft-700 px-1.5 py-0.5 text-xs text-mineshaft-400">
Gateway v{el.isV1 ? "1" : "2"}
</span>
</div>
</Td>
<Td>{el.identity.name}</Td>
<Td>
{el.heartbeat
@@ -176,20 +186,22 @@ export const GatewayListPage = withPermission(
>
Copy ID
</DropdownMenuItem>
<OrgPermissionCan
I={OrgGatewayPermissionActions.EditGateways}
a={OrgPermissionSubjects.Gateway}
>
{(isAllowed: boolean) => (
<DropdownMenuItem
isDisabled={!isAllowed}
icon={<FontAwesomeIcon icon={faEdit} />}
onClick={() => handlePopUpOpen("editDetails", el)}
>
Edit Details
</DropdownMenuItem>
)}
</OrgPermissionCan>
{el.isV1 && (
<OrgPermissionCan
I={OrgGatewayPermissionActions.EditGateways}
a={OrgPermissionSubjects.Gateway}
>
{(isAllowed: boolean) => (
<DropdownMenuItem
isDisabled={!isAllowed}
icon={<FontAwesomeIcon icon={faEdit} />}
onClick={() => handlePopUpOpen("editDetails", el)}
>
Edit Details
</DropdownMenuItem>
)}
</OrgPermissionCan>
)}
<OrgPermissionCan
I={OrgPermissionAppConnectionActions.Delete}
a={OrgPermissionSubjects.AppConnections}
@@ -224,7 +236,7 @@ export const GatewayListPage = withPermission(
/>
</ModalContent>
</Modal>
{!isGatewayLoading && !filteredGateway?.length && (
{!isGatewaysLoading && !filteredGateway?.length && (
<EmptyState
title={
gateways?.length
@@ -251,8 +263,5 @@ export const GatewayListPage = withPermission(
</div>
);
},
{
action: OrgPermissionAppConnectionActions.Read,
subject: OrgPermissionSubjects.AppConnections
}
{ action: OrgGatewayPermissionActions.ListGateways, subject: OrgPermissionSubjects.Gateway }
);