Refactor RDP proxy handling and update related tests
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -4,8 +4,8 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Панель Secure Access Fabric</title>
|
||||
<script type="module" crossorigin src="/assets/index-DU4b34gj.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-BeytaWKC.css">
|
||||
<script type="module" crossorigin src="/assets/index-gMV--oab.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Cur_BAkX.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
+579
-498
File diff suppressed because it is too large
Load Diff
@@ -9,10 +9,6 @@ import type {
|
||||
ClusterAuthorityState,
|
||||
ClusterNode,
|
||||
CreatedJoinToken,
|
||||
FabricEntryPoint,
|
||||
FabricEntryPointNode,
|
||||
FabricEgressPool,
|
||||
FabricEgressPoolNode,
|
||||
FabricServiceChannelAdaptivePolicy,
|
||||
FabricServiceChannelBreadcrumbWindowPolicy,
|
||||
FabricServiceChannelPoolPolicy,
|
||||
@@ -177,28 +173,6 @@ export type UpsertResourceSecretPayload = {
|
||||
domain?: string;
|
||||
};
|
||||
|
||||
export type CreateFabricEntryPointPayload = {
|
||||
name: string;
|
||||
endpointType: string;
|
||||
publicEndpoint?: string | null;
|
||||
policy?: Record<string, unknown>;
|
||||
metadata?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type CreateFabricEgressPoolPayload = {
|
||||
name: string;
|
||||
description?: string | null;
|
||||
routeScope?: Record<string, unknown>;
|
||||
policy?: Record<string, unknown>;
|
||||
metadata?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type SetFabricEndpointNodePayload = {
|
||||
status?: string;
|
||||
priority?: number;
|
||||
metadata?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export class AdminApiClient {
|
||||
private readonly baseUrl: string;
|
||||
private readonly actorUserId: string;
|
||||
@@ -1021,92 +995,6 @@ export class AdminApiClient {
|
||||
return payload.qos_policies ?? [];
|
||||
}
|
||||
|
||||
async listFabricEntryPoints(clusterId: string): Promise<FabricEntryPoint[]> {
|
||||
const payload = await this.get<{ entry_points: FabricEntryPoint[] }>(`/clusters/${clusterId}/fabric/entry-points`);
|
||||
return payload.entry_points ?? [];
|
||||
}
|
||||
|
||||
async createFabricEntryPoint(clusterId: string, input: CreateFabricEntryPointPayload): Promise<FabricEntryPoint> {
|
||||
const payload = await this.post<{ entry_point: FabricEntryPoint }>(`/clusters/${clusterId}/fabric/entry-points`, {
|
||||
actor_user_id: this.actorUserId,
|
||||
name: input.name,
|
||||
status: "active",
|
||||
endpoint_type: input.endpointType || "client_access",
|
||||
public_endpoint: input.publicEndpoint || null,
|
||||
policy: input.policy || {},
|
||||
metadata: input.metadata || {},
|
||||
});
|
||||
return payload.entry_point;
|
||||
}
|
||||
|
||||
async listFabricEntryPointNodes(clusterId: string, entryPointId: string): Promise<FabricEntryPointNode[]> {
|
||||
const payload = await this.get<{ entry_point_nodes: FabricEntryPointNode[] }>(
|
||||
`/clusters/${clusterId}/fabric/entry-points/${entryPointId}/nodes`,
|
||||
);
|
||||
return payload.entry_point_nodes ?? [];
|
||||
}
|
||||
|
||||
async setFabricEntryPointNode(
|
||||
clusterId: string,
|
||||
entryPointId: string,
|
||||
nodeId: string,
|
||||
input: SetFabricEndpointNodePayload = {},
|
||||
): Promise<FabricEntryPointNode> {
|
||||
const payload = await this.put<{ entry_point_node: FabricEntryPointNode }>(
|
||||
`/clusters/${clusterId}/fabric/entry-points/${entryPointId}/nodes/${nodeId}`,
|
||||
{
|
||||
actor_user_id: this.actorUserId,
|
||||
status: input.status || "active",
|
||||
priority: input.priority || 100,
|
||||
metadata: input.metadata || {},
|
||||
},
|
||||
);
|
||||
return payload.entry_point_node;
|
||||
}
|
||||
|
||||
async listFabricEgressPools(clusterId: string): Promise<FabricEgressPool[]> {
|
||||
const payload = await this.get<{ egress_pools: FabricEgressPool[] }>(`/clusters/${clusterId}/fabric/egress-pools`);
|
||||
return payload.egress_pools ?? [];
|
||||
}
|
||||
|
||||
async createFabricEgressPool(clusterId: string, input: CreateFabricEgressPoolPayload): Promise<FabricEgressPool> {
|
||||
const payload = await this.post<{ egress_pool: FabricEgressPool }>(`/clusters/${clusterId}/fabric/egress-pools`, {
|
||||
actor_user_id: this.actorUserId,
|
||||
name: input.name,
|
||||
status: "active",
|
||||
description: input.description || null,
|
||||
route_scope: input.routeScope || {},
|
||||
policy: input.policy || {},
|
||||
metadata: input.metadata || {},
|
||||
});
|
||||
return payload.egress_pool;
|
||||
}
|
||||
|
||||
async listFabricEgressPoolNodes(clusterId: string, egressPoolId: string): Promise<FabricEgressPoolNode[]> {
|
||||
const payload = await this.get<{ egress_pool_nodes: FabricEgressPoolNode[] }>(
|
||||
`/clusters/${clusterId}/fabric/egress-pools/${egressPoolId}/nodes`,
|
||||
);
|
||||
return payload.egress_pool_nodes ?? [];
|
||||
}
|
||||
|
||||
async setFabricEgressPoolNode(
|
||||
clusterId: string,
|
||||
egressPoolId: string,
|
||||
nodeId: string,
|
||||
input: SetFabricEndpointNodePayload = {},
|
||||
): Promise<FabricEgressPoolNode> {
|
||||
const payload = await this.put<{ egress_pool_node: FabricEgressPoolNode }>(
|
||||
`/clusters/${clusterId}/fabric/egress-pools/${egressPoolId}/nodes/${nodeId}`,
|
||||
{
|
||||
actor_user_id: this.actorUserId,
|
||||
status: input.status || "active",
|
||||
priority: input.priority || 100,
|
||||
metadata: input.metadata || {},
|
||||
},
|
||||
);
|
||||
return payload.egress_pool_node;
|
||||
}
|
||||
|
||||
async listVPNConnections(clusterId: string): Promise<VPNConnection[]> {
|
||||
const payload = await this.get<{ vpn_connections: VPNConnection[] }>(`/clusters/${clusterId}/vpn-connections`);
|
||||
return payload.vpn_connections ?? [];
|
||||
|
||||
+168
-43
@@ -227,7 +227,7 @@ button.danger {
|
||||
}
|
||||
|
||||
.workspace {
|
||||
width: min(1500px, calc(100vw - 340px));
|
||||
width: calc(100vw - 310px);
|
||||
padding: 28px 28px 54px;
|
||||
}
|
||||
|
||||
@@ -406,6 +406,33 @@ summary {
|
||||
grid-template-columns: repeat(2, minmax(280px, 1fr));
|
||||
}
|
||||
|
||||
.fabricTransportView {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.fabricMapCard {
|
||||
min-height: calc(100vh - 176px);
|
||||
}
|
||||
|
||||
.fabricMapCard .cardHead {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.fabricMapCard .topologyShell {
|
||||
min-height: calc(100vh - 310px);
|
||||
}
|
||||
|
||||
.fabricMapCard .topologySvg {
|
||||
min-height: calc(100vh - 360px);
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.fabricDiagnostics {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.span2 {
|
||||
grid-column: span 2;
|
||||
}
|
||||
@@ -983,54 +1010,32 @@ summary {
|
||||
|
||||
.topologySvg {
|
||||
width: 100%;
|
||||
min-height: 520px;
|
||||
min-height: 640px;
|
||||
max-height: 78vh;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 22px;
|
||||
color: rgba(24, 32, 24, 0.42);
|
||||
background:
|
||||
radial-gradient(circle at 50% 50%, rgba(47, 111, 79, 0.12), transparent 18rem),
|
||||
linear-gradient(135deg, rgba(255, 255, 255, 0.56), rgba(255, 250, 240, 0.32));
|
||||
}
|
||||
|
||||
.topologyRing {
|
||||
fill: none;
|
||||
stroke: rgba(47, 111, 79, 0.14);
|
||||
stroke-width: 2;
|
||||
stroke-dasharray: 12 10;
|
||||
}
|
||||
|
||||
.topologyZone {
|
||||
fill: rgba(255, 252, 239, 0.44);
|
||||
stroke: rgba(24, 32, 24, 0.08);
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.topologyZone.ingress {
|
||||
fill: rgba(67, 122, 146, 0.11);
|
||||
}
|
||||
|
||||
.topologyZone.core {
|
||||
fill: rgba(47, 111, 79, 0.09);
|
||||
}
|
||||
|
||||
.topologyZone.egress {
|
||||
fill: rgba(176, 122, 50, 0.11);
|
||||
}
|
||||
|
||||
.topologyLayerLabel {
|
||||
fill: var(--green);
|
||||
font-size: 22px;
|
||||
font-weight: 950;
|
||||
letter-spacing: 0.08em;
|
||||
text-anchor: middle;
|
||||
text-transform: uppercase;
|
||||
linear-gradient(rgba(24, 32, 24, 0.035) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(24, 32, 24, 0.035) 1px, transparent 1px),
|
||||
linear-gradient(135deg, rgba(255, 255, 255, 0.56), rgba(248, 250, 246, 0.42));
|
||||
background-size: 34px 34px, 34px 34px, auto;
|
||||
}
|
||||
|
||||
.topologyLink {
|
||||
stroke-width: 4;
|
||||
stroke-width: 3;
|
||||
stroke-linecap: round;
|
||||
opacity: 0.84;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.topologyLinkGroup {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.topologyLinkGroup:hover .topologyLink {
|
||||
stroke-width: 5;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.topologyLink.good {
|
||||
@@ -1049,6 +1054,17 @@ summary {
|
||||
stroke-dasharray: 4 7;
|
||||
}
|
||||
|
||||
.topologyLink.relay {
|
||||
stroke-dasharray: 8 7;
|
||||
}
|
||||
|
||||
.topologyLink.route {
|
||||
color: var(--steel);
|
||||
stroke: var(--steel);
|
||||
stroke-dasharray: 3 8;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.topologyLink.bad {
|
||||
color: var(--red);
|
||||
stroke: var(--red);
|
||||
@@ -1105,7 +1121,7 @@ summary {
|
||||
|
||||
.topologyLinkLabel {
|
||||
fill: var(--muted);
|
||||
font-size: 22px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.topologyEndpointRect {
|
||||
@@ -1140,14 +1156,53 @@ summary {
|
||||
.topologyNodeCircle {
|
||||
fill: rgba(255, 252, 239, 0.94);
|
||||
stroke: var(--steel);
|
||||
stroke-width: 4;
|
||||
stroke-width: 3;
|
||||
filter: drop-shadow(0 14px 22px rgba(24, 32, 24, 0.14));
|
||||
}
|
||||
|
||||
.topologyNode {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.topologyNode:hover .topologyNodeCircle {
|
||||
stroke-width: 5;
|
||||
filter: drop-shadow(0 16px 26px rgba(24, 32, 24, 0.22));
|
||||
}
|
||||
|
||||
.topologyNodeCircle.healthy {
|
||||
stroke: var(--green);
|
||||
}
|
||||
|
||||
.topologyNodeCircle.active {
|
||||
stroke-width: 5;
|
||||
}
|
||||
|
||||
.topologyNodeCircle.passive {
|
||||
stroke: var(--amber);
|
||||
stroke-dasharray: 10 7;
|
||||
}
|
||||
|
||||
.topologyNodeCircle.mixed {
|
||||
stroke: var(--steel);
|
||||
stroke-dasharray: 16 6 4 6;
|
||||
}
|
||||
|
||||
.topologyNodeCircle.unknown {
|
||||
stroke-dasharray: 4 8;
|
||||
}
|
||||
|
||||
.topologyNodeCircle.web-ready {
|
||||
fill: rgba(232, 248, 239, 0.98);
|
||||
}
|
||||
|
||||
.topologyNodeCircle.web-degraded {
|
||||
fill: rgba(255, 246, 224, 0.98);
|
||||
}
|
||||
|
||||
.topologyNodeCircle.web-blocked {
|
||||
fill: rgba(255, 235, 230, 0.98);
|
||||
}
|
||||
|
||||
.topologyNodeCircle.critical,
|
||||
.topologyNodeCircle.offline,
|
||||
.topologyNodeCircle.failed {
|
||||
@@ -1156,12 +1211,12 @@ summary {
|
||||
|
||||
.topologyNodeName {
|
||||
fill: var(--ink);
|
||||
font-size: 23px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.topologyNodeMeta {
|
||||
fill: var(--muted);
|
||||
font-size: 18px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.topologyEmpty {
|
||||
@@ -1169,6 +1224,36 @@ summary {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.topologyTooltipObject {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.topologyTooltip {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
max-width: 360px;
|
||||
padding: 12px 14px;
|
||||
color: var(--ink);
|
||||
background: rgba(255, 252, 239, 0.96);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 18px 42px rgba(24, 32, 24, 0.18);
|
||||
font-size: 13px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.topologyTooltip strong,
|
||||
.topologyTooltip span {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.topologyTooltip strong {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.topologyLegend {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -1205,6 +1290,16 @@ summary {
|
||||
border-color: var(--amber);
|
||||
}
|
||||
|
||||
.legendLine.relay {
|
||||
border-top-style: dashed;
|
||||
border-color: var(--amber);
|
||||
}
|
||||
|
||||
.legendLine.route {
|
||||
border-top-style: dashed;
|
||||
border-color: var(--steel);
|
||||
}
|
||||
|
||||
.legendLine.stale,
|
||||
.legendLine.problem {
|
||||
border-top-style: dashed;
|
||||
@@ -1216,12 +1311,42 @@ summary {
|
||||
border-color: var(--steel);
|
||||
}
|
||||
|
||||
.legendDot {
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.legendDot.webReady {
|
||||
background: rgba(232, 248, 239, 0.98);
|
||||
}
|
||||
|
||||
.legendDot.webDegraded {
|
||||
background: rgba(255, 246, 224, 0.98);
|
||||
}
|
||||
|
||||
.legendDot.webBlocked {
|
||||
background: rgba(255, 235, 230, 0.98);
|
||||
}
|
||||
|
||||
.serviceTags {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.sectionBlock {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.sectionBlock h4 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.serviceTag {
|
||||
display: grid;
|
||||
gap: 5px;
|
||||
|
||||
@@ -1210,56 +1210,6 @@ export type NodeSyntheticMeshConfig = {
|
||||
production_forwarding: boolean;
|
||||
};
|
||||
|
||||
export type FabricEntryPoint = {
|
||||
id: string;
|
||||
cluster_id: string;
|
||||
name: string;
|
||||
status: string;
|
||||
endpoint_type: string;
|
||||
public_endpoint?: string | null;
|
||||
policy?: Record<string, unknown>;
|
||||
metadata?: Record<string, unknown>;
|
||||
created_by_user_id?: string | null;
|
||||
created_at: string;
|
||||
updated_at?: string;
|
||||
};
|
||||
|
||||
export type FabricEntryPointNode = {
|
||||
entry_point_id: string;
|
||||
cluster_id: string;
|
||||
node_id: string;
|
||||
status: string;
|
||||
priority: number;
|
||||
metadata?: Record<string, unknown>;
|
||||
added_by_user_id?: string | null;
|
||||
added_at: string;
|
||||
};
|
||||
|
||||
export type FabricEgressPool = {
|
||||
id: string;
|
||||
cluster_id: string;
|
||||
name: string;
|
||||
status: string;
|
||||
description?: string | null;
|
||||
route_scope?: Record<string, unknown>;
|
||||
policy?: Record<string, unknown>;
|
||||
metadata?: Record<string, unknown>;
|
||||
created_by_user_id?: string | null;
|
||||
created_at: string;
|
||||
updated_at?: string;
|
||||
};
|
||||
|
||||
export type FabricEgressPoolNode = {
|
||||
egress_pool_id: string;
|
||||
cluster_id: string;
|
||||
node_id: string;
|
||||
status: string;
|
||||
priority: number;
|
||||
metadata?: Record<string, unknown>;
|
||||
added_by_user_id?: string | null;
|
||||
added_at: string;
|
||||
};
|
||||
|
||||
export type QoSPolicy = {
|
||||
id: string;
|
||||
cluster_id: string;
|
||||
|
||||
Reference in New Issue
Block a user