@openziti/ziti-console-lib 1.3.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/openziti-ziti-console-lib.mjs +553 -159
- package/fesm2022/openziti-ziti-console-lib.mjs.map +1 -1
- package/index.d.ts +11 -0
- package/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -6955,7 +6955,7 @@ class EdgeRouterFormService {
|
|
|
6955
6955
|
this.growlerService.show(growlerData);
|
|
6956
6956
|
}
|
|
6957
6957
|
getAssociatedServices(id) {
|
|
6958
|
-
this.zitiService.getSubdata('edge-routers', id, 'services').then((result) => {
|
|
6958
|
+
this.zitiService.getSubdata('edge-routers', id, 'services', this.zitiService.DEFAULT_PAGING).then((result) => {
|
|
6959
6959
|
this.associatedServices = result.data;
|
|
6960
6960
|
this.associatedServiceNames = this.associatedServices.map((svc) => {
|
|
6961
6961
|
return svc.name;
|
|
@@ -6963,7 +6963,7 @@ class EdgeRouterFormService {
|
|
|
6963
6963
|
});
|
|
6964
6964
|
}
|
|
6965
6965
|
getAssociatedIdentities(id) {
|
|
6966
|
-
this.zitiService.getSubdata('edge-routers', id, 'identities').then((result) => {
|
|
6966
|
+
this.zitiService.getSubdata('edge-routers', id, 'identities', this.zitiService.DEFAULT_PAGING).then((result) => {
|
|
6967
6967
|
this.associatedIdentities = result.data;
|
|
6968
6968
|
this.associatedIdentityNames = this.associatedIdentities.map((policy) => {
|
|
6969
6969
|
return policy.name;
|
|
@@ -10742,6 +10742,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
10742
10742
|
|
|
10743
10743
|
class Link {
|
|
10744
10744
|
constructor() {
|
|
10745
|
+
this.statusReason = null;
|
|
10745
10746
|
this.linkType = 'inferred';
|
|
10746
10747
|
}
|
|
10747
10748
|
}
|
|
@@ -10829,6 +10830,7 @@ class IdentityServicePathHelper {
|
|
|
10829
10830
|
srNode.id = selectedServiceOb.id;
|
|
10830
10831
|
srNode.name = selectedServiceOb.name;
|
|
10831
10832
|
srNode.intercept = serviceConfigAlias(serviceConfigs);
|
|
10833
|
+
buildServiceDetails(srNode, selectedServiceOb, serviceConfigs);
|
|
10832
10834
|
srNode.type = 'Config Service';
|
|
10833
10835
|
srNode.group = '4';
|
|
10834
10836
|
srNode.status = '1';
|
|
@@ -10932,6 +10934,36 @@ class IdentityServicePathHelper {
|
|
|
10932
10934
|
}
|
|
10933
10935
|
}
|
|
10934
10936
|
});
|
|
10937
|
+
// Per-host connectivity is derived from the identity's own controller-reported state
|
|
10938
|
+
// (hasEdgeRouterConnection / edgeRouterConnectionStatus -> routerConnection/apiSession),
|
|
10939
|
+
// NOT from terminators: real ziti terminators carry no hosting-identity id (identity:"",
|
|
10940
|
+
// no hostId), so a terminator can't be mapped back to the host that created it. Terminator
|
|
10941
|
+
// COUNT remains a service-level health hint only.
|
|
10942
|
+
// Annotate node connectivity so the renderer can pick a broken icon and the tooltip can
|
|
10943
|
+
// explain the cause. A hosting tunneler that is enrolled but unreachable, or expected to
|
|
10944
|
+
// host (Bind policy) yet has no terminator, is "broken".
|
|
10945
|
+
group3Nodes.forEach((node) => {
|
|
10946
|
+
if (node.status === 'Un-Registered') {
|
|
10947
|
+
node.brokenCause = 'misconfigured';
|
|
10948
|
+
node.connectivity = 'Not enrolled';
|
|
10949
|
+
}
|
|
10950
|
+
else if (node.apiSession === 'No' || node.routerConnection === 'No') {
|
|
10951
|
+
node.brokenCause = 'tunneler-unreachable';
|
|
10952
|
+
node.connectivity = 'Configured but not connected (offline or blocked)';
|
|
10953
|
+
}
|
|
10954
|
+
else {
|
|
10955
|
+
node.connectivity = 'Connected';
|
|
10956
|
+
}
|
|
10957
|
+
});
|
|
10958
|
+
if (endpointNodeOb && endpointNodeOb.status !== 'Un-Registered') {
|
|
10959
|
+
if (endpointNodeOb.apiSession === 'No' || endpointNodeOb.routerConnection === 'No') {
|
|
10960
|
+
endpointNodeOb.brokenCause = 'tunneler-unreachable';
|
|
10961
|
+
endpointNodeOb.connectivity = 'Configured but not connected (offline or blocked)';
|
|
10962
|
+
}
|
|
10963
|
+
else {
|
|
10964
|
+
endpointNodeOb.connectivity = 'Connected';
|
|
10965
|
+
}
|
|
10966
|
+
}
|
|
10935
10967
|
// Build fabric link lookup
|
|
10936
10968
|
const fabricLinkSet = new Set();
|
|
10937
10969
|
fabricLinks.forEach((link) => {
|
|
@@ -10950,8 +10982,7 @@ class IdentityServicePathHelper {
|
|
|
10950
10982
|
lnk.sourceName = endpointNodeOb.name;
|
|
10951
10983
|
lnk.target = nd.id;
|
|
10952
10984
|
lnk.targetName = nd.name;
|
|
10953
|
-
lnk
|
|
10954
|
-
lnk.weight = getWeight(lnk.status);
|
|
10985
|
+
applyLinkState(lnk, getEndpointToRouterLinkState(endpointNodeOb, nd));
|
|
10955
10986
|
lnk.linkType = activeCircuitHops.has(hopKey(endpointNodeOb.id, nd.id))
|
|
10956
10987
|
? 'active-circuit' : 'endpoint-connection';
|
|
10957
10988
|
rootOb.addLink(lnk);
|
|
@@ -11055,11 +11086,17 @@ class IdentityServicePathHelper {
|
|
|
11055
11086
|
lnk.target = r2.id;
|
|
11056
11087
|
lnk.targetName = r2.name;
|
|
11057
11088
|
lnk.linkType = isActiveCircuit ? 'active-circuit' : 'fabric-link';
|
|
11058
|
-
|
|
11059
|
-
|
|
11060
|
-
if (isActiveCircuit)
|
|
11061
|
-
lnk
|
|
11062
|
-
|
|
11089
|
+
const r1Up = r1.online === 'Yes' || r1.status === 'PROVISIONED';
|
|
11090
|
+
const r2Up = r2.online === 'Yes' || r2.status === 'PROVISIONED';
|
|
11091
|
+
if (isActiveCircuit) {
|
|
11092
|
+
applyLinkState(lnk, { state: 1, reason: null });
|
|
11093
|
+
}
|
|
11094
|
+
else if (!r1Up || !r2Up) {
|
|
11095
|
+
applyLinkState(lnk, { state: 2, reason: 'router-down' });
|
|
11096
|
+
}
|
|
11097
|
+
else {
|
|
11098
|
+
applyLinkState(lnk, { state: 1, reason: null });
|
|
11099
|
+
}
|
|
11063
11100
|
rootOb.addLink(lnk);
|
|
11064
11101
|
});
|
|
11065
11102
|
// If no fabric links connect dial to host side, add inferred connections
|
|
@@ -11108,8 +11145,7 @@ class IdentityServicePathHelper {
|
|
|
11108
11145
|
lnk.sourceName = routerNode.name;
|
|
11109
11146
|
lnk.target = hostNode.id;
|
|
11110
11147
|
lnk.targetName = hostNode.name;
|
|
11111
|
-
lnk
|
|
11112
|
-
lnk.weight = getWeight(lnk.status);
|
|
11148
|
+
applyLinkState(lnk, getEndpointToRouterLinkState(hostNode, routerNode));
|
|
11113
11149
|
lnk.linkType = activeCircuitHops.has(hopKey(routerNode.id, hostNode.id))
|
|
11114
11150
|
? 'active-circuit' : 'endpoint-connection';
|
|
11115
11151
|
rootOb.addLink(lnk);
|
|
@@ -11126,8 +11162,7 @@ class IdentityServicePathHelper {
|
|
|
11126
11162
|
lnk.sourceName = routerNode.name;
|
|
11127
11163
|
lnk.target = hostNode.id;
|
|
11128
11164
|
lnk.targetName = hostNode.name;
|
|
11129
|
-
lnk
|
|
11130
|
-
lnk.weight = getWeight(lnk.status);
|
|
11165
|
+
applyLinkState(lnk, getEndpointToRouterLinkState(hostNode, routerNode));
|
|
11131
11166
|
lnk.linkType = activeCircuitHops.has(hopKey(routerNode.id, hostNode.id))
|
|
11132
11167
|
? 'active-circuit'
|
|
11133
11168
|
: (lnk.status === 1 ? 'endpoint-connection' : 'inferred');
|
|
@@ -11150,8 +11185,7 @@ class IdentityServicePathHelper {
|
|
|
11150
11185
|
lnk.sourceName = dialRouter.name;
|
|
11151
11186
|
lnk.target = hostNode.id;
|
|
11152
11187
|
lnk.targetName = hostNode.name;
|
|
11153
|
-
lnk
|
|
11154
|
-
lnk.weight = getWeight(lnk.status);
|
|
11188
|
+
applyLinkState(lnk, getEndpointToRouterLinkState(hostNode, dialRouter));
|
|
11155
11189
|
lnk.linkType = lnk.status === 1 ? 'endpoint-connection' : 'inferred';
|
|
11156
11190
|
rootOb.addLink(lnk);
|
|
11157
11191
|
}
|
|
@@ -11178,8 +11212,7 @@ class IdentityServicePathHelper {
|
|
|
11178
11212
|
lnk.target = serviceNode.id;
|
|
11179
11213
|
lnk.targetName = serviceNode.name;
|
|
11180
11214
|
lnk.weight = 1;
|
|
11181
|
-
lnk
|
|
11182
|
-
lnk.weight = getWeight(lnk.status);
|
|
11215
|
+
applyLinkState(lnk, getServiceToEndpointLinkState(hostNode, serviceNode));
|
|
11183
11216
|
lnk.linkType = activeCircuitHops.has(hopKey(hostNode.id, serviceNode.id))
|
|
11184
11217
|
? 'active-circuit' : 'endpoint-connection';
|
|
11185
11218
|
rootOb.addLink(lnk);
|
|
@@ -11199,6 +11232,28 @@ class IdentityServicePathHelper {
|
|
|
11199
11232
|
return false;
|
|
11200
11233
|
return true;
|
|
11201
11234
|
});
|
|
11235
|
+
// Collapse parallel links between the same node pair. A router that is on BOTH the dial
|
|
11236
|
+
// and host side can otherwise produce two links to the same node — e.g. an 'available'
|
|
11237
|
+
// inferred link and a 'router-down' link — which overlap and render as mixed dashed/dotted.
|
|
11238
|
+
// Keep the single most-significant link per pair.
|
|
11239
|
+
const linkSeverity = (l) => {
|
|
11240
|
+
if (l.linkType === 'active-circuit')
|
|
11241
|
+
return 4; // live traffic wins
|
|
11242
|
+
if (l.status === 2)
|
|
11243
|
+
return 3; // broken (router-down / tunneler-unreachable)
|
|
11244
|
+
if (l.status === 1)
|
|
11245
|
+
return 2; // connected
|
|
11246
|
+
return 1; // available / inferred
|
|
11247
|
+
};
|
|
11248
|
+
const bestLinkByPair = new Map();
|
|
11249
|
+
rootOb.links.forEach((lnk) => {
|
|
11250
|
+
const key = [lnk.source, lnk.target].sort().join('::');
|
|
11251
|
+
const existing = bestLinkByPair.get(key);
|
|
11252
|
+
if (!existing || linkSeverity(lnk) > linkSeverity(existing)) {
|
|
11253
|
+
bestLinkByPair.set(key, lnk);
|
|
11254
|
+
}
|
|
11255
|
+
});
|
|
11256
|
+
rootOb.links = Array.from(bestLinkByPair.values());
|
|
11202
11257
|
return rootOb;
|
|
11203
11258
|
}
|
|
11204
11259
|
/**
|
|
@@ -11236,6 +11291,7 @@ class IdentityServicePathHelper {
|
|
|
11236
11291
|
srNode.id = selectedServiceOb.id;
|
|
11237
11292
|
srNode.name = selectedServiceOb.name;
|
|
11238
11293
|
srNode.intercept = serviceConfigAlias(serviceConfigs);
|
|
11294
|
+
buildServiceDetails(srNode, selectedServiceOb, serviceConfigs);
|
|
11239
11295
|
srNode.type = 'Config Service';
|
|
11240
11296
|
srNode.group = '4';
|
|
11241
11297
|
srNode.status = '1';
|
|
@@ -11335,8 +11391,7 @@ class IdentityServicePathHelper {
|
|
|
11335
11391
|
lnk.sourceName = endpointNodeOb.name;
|
|
11336
11392
|
lnk.target = nd.id;
|
|
11337
11393
|
lnk.targetName = nd.name;
|
|
11338
|
-
lnk
|
|
11339
|
-
lnk.weight = getWeight(lnk.status);
|
|
11394
|
+
applyLinkState(lnk, getEndpointToRouterLinkState(endpointNodeOb, nd));
|
|
11340
11395
|
lnk.linkType = 'endpoint-connection';
|
|
11341
11396
|
rootOb.addLink(lnk);
|
|
11342
11397
|
}
|
|
@@ -11361,9 +11416,10 @@ class IdentityServicePathHelper {
|
|
|
11361
11416
|
nd2.status === 'PROVISIONED' &&
|
|
11362
11417
|
nd2.online === 'Yes'
|
|
11363
11418
|
? 1 : 0;
|
|
11419
|
+
lnk.statusReason = lnk.status === 1 ? null : 'router-down';
|
|
11364
11420
|
}
|
|
11365
11421
|
else {
|
|
11366
|
-
lnk
|
|
11422
|
+
applyLinkState(lnk, getEndpointToRouterLinkState(nd2, nd1));
|
|
11367
11423
|
}
|
|
11368
11424
|
lnk.weight = getWeight(lnk.status);
|
|
11369
11425
|
lnk.linkType = 'inferred';
|
|
@@ -11386,8 +11442,7 @@ class IdentityServicePathHelper {
|
|
|
11386
11442
|
lnk.status = foundNd.online === 'Yes' ? 1 : -1;
|
|
11387
11443
|
}
|
|
11388
11444
|
else {
|
|
11389
|
-
lnk
|
|
11390
|
-
lnk.weight = getWeight(lnk.status);
|
|
11445
|
+
applyLinkState(lnk, getServiceToEndpointLinkState(group3Nodes[k1], group4Nodes[k0]));
|
|
11391
11446
|
}
|
|
11392
11447
|
lnk.linkType = 'endpoint-connection';
|
|
11393
11448
|
rootOb.addLink(lnk);
|
|
@@ -11455,6 +11510,44 @@ function serviceConfigAlias(configs) {
|
|
|
11455
11510
|
});
|
|
11456
11511
|
return intercept;
|
|
11457
11512
|
}
|
|
11513
|
+
// Annotates the service node with labeled, purpose-specific details so the hover card can explain
|
|
11514
|
+
// what each address is for and surface the service's configuration. Handles services that are
|
|
11515
|
+
// intercept+host, host-only, intercept-only, or SDK-defined (no address configs at all).
|
|
11516
|
+
function buildServiceDetails(srNode, service, configs) {
|
|
11517
|
+
srNode.encryptionRequired = service ? service.encryptionRequired : undefined;
|
|
11518
|
+
srNode.terminatorStrategy = service ? service.terminatorStrategy : undefined;
|
|
11519
|
+
srNode.serviceRoleAttributes = _.isArray(service && service.roleAttributes) ? service.roleAttributes : [];
|
|
11520
|
+
srNode.permissions = _.isArray(service && service.permissions) ? service.permissions : [];
|
|
11521
|
+
const fmtPorts = (portRanges, port) => {
|
|
11522
|
+
if (_.isArray(portRanges)) {
|
|
11523
|
+
return portRanges.map((r) => (r.low === r.high ? `${r.low}` : `${r.low}-${r.high}`));
|
|
11524
|
+
}
|
|
11525
|
+
if (port !== undefined && port !== null)
|
|
11526
|
+
return [String(port)];
|
|
11527
|
+
return [];
|
|
11528
|
+
};
|
|
11529
|
+
const fmtEndpoint = (addrs, ports, protos) => {
|
|
11530
|
+
const a = (addrs || []).filter(Boolean).join(', ');
|
|
11531
|
+
const detail = [(protos || []).join('/'), (ports || []).join(',')].filter(Boolean).join('/');
|
|
11532
|
+
return detail ? `${a} ${detail}` : a;
|
|
11533
|
+
};
|
|
11534
|
+
const types = [];
|
|
11535
|
+
_.isArray(configs) && configs.forEach((conf) => {
|
|
11536
|
+
const t = ((conf && conf.configType && conf.configType.name) || (conf && conf.type) || '').toString();
|
|
11537
|
+
const d = (conf && conf.data) || {};
|
|
11538
|
+
if (t)
|
|
11539
|
+
types.push(t);
|
|
11540
|
+
if (t.indexOf('intercept') === 0) {
|
|
11541
|
+
const addrs = d.addresses || (d.address ? [d.address] : []);
|
|
11542
|
+
srNode.interceptSummary = fmtEndpoint(addrs, fmtPorts(d.portRanges, d.port), d.protocols || (d.protocol ? [d.protocol] : []));
|
|
11543
|
+
}
|
|
11544
|
+
else if (t.indexOf('host') === 0) {
|
|
11545
|
+
const addrs = d.address ? [d.address] : (d.forwardAddress ? ['(forwarded)'] : []);
|
|
11546
|
+
srNode.hostSummary = fmtEndpoint(addrs, fmtPorts(d.allowedPortRanges, d.port), d.protocol ? [d.protocol] : (d.protocols || (d.forwardProtocol ? ['(forwarded)'] : [])));
|
|
11547
|
+
}
|
|
11548
|
+
});
|
|
11549
|
+
srNode.configTypeNames = types;
|
|
11550
|
+
}
|
|
11458
11551
|
function findEndpoint(endpointId, endpoints) {
|
|
11459
11552
|
if (endpointId === null) {
|
|
11460
11553
|
return null;
|
|
@@ -11474,7 +11567,10 @@ function createEndpoint(ePoint) {
|
|
|
11474
11567
|
node.id = ePoint.id;
|
|
11475
11568
|
node.name = ePoint.name;
|
|
11476
11569
|
node.type = 'Identity';
|
|
11477
|
-
|
|
11570
|
+
// Prefer the explicit booleans, which controllers populate reliably. Only fall back to the
|
|
11571
|
+
// edgeRouterConnectionStatus string when it actually carries a value — note its key can be
|
|
11572
|
+
// PRESENT-BUT-NULL, so `_.has` is not a safe guard (that bug made every identity look "Yes").
|
|
11573
|
+
if (ePoint.edgeRouterConnectionStatus != null) {
|
|
11478
11574
|
node.routerConnection = ePoint.edgeRouterConnectionStatus === 'offline' ? 'No' : 'Yes';
|
|
11479
11575
|
node.apiSession = node.routerConnection;
|
|
11480
11576
|
}
|
|
@@ -11484,7 +11580,27 @@ function createEndpoint(ePoint) {
|
|
|
11484
11580
|
}
|
|
11485
11581
|
node.os = ePoint.envInfo ? ePoint.envInfo.os : '';
|
|
11486
11582
|
node.mfaEnabled = ePoint.isMfaEnabled;
|
|
11487
|
-
|
|
11583
|
+
// "Registered" means the identity has enrolled and run (it has reported env info). Guard on a
|
|
11584
|
+
// truthy os: the controller returns envInfo as an empty object {} for a created-but-never-
|
|
11585
|
+
// enrolled identity, and `{}.os !== null` is true — which previously mis-classed it Registered.
|
|
11586
|
+
node.status = (ePoint.envInfo && ePoint.envInfo.os) ? 'Registered' : 'Un-Registered';
|
|
11587
|
+
// Node status indicator. Mirrors the controller's edgeRouterConnectionStatus enum
|
|
11588
|
+
// (online | offline | unknown) when present, falling back to the hasEdgeRouterConnection
|
|
11589
|
+
// boolean otherwise. An identity that hasn't enrolled is "unenrolled" (its own state, not an
|
|
11590
|
+
// error). There is no "error" value in the API, so we don't synthesize one.
|
|
11591
|
+
if (node.status === 'Un-Registered') {
|
|
11592
|
+
node.connState = 'unenrolled';
|
|
11593
|
+
}
|
|
11594
|
+
else {
|
|
11595
|
+
const ers = ePoint.edgeRouterConnectionStatus;
|
|
11596
|
+
if (ers === 'online' || ers === 'offline' || ers === 'unknown') {
|
|
11597
|
+
node.connState = ers;
|
|
11598
|
+
}
|
|
11599
|
+
else {
|
|
11600
|
+
node.connState = (ePoint.hasEdgeRouterConnection === true || ePoint.hasApiSession === true)
|
|
11601
|
+
? 'online' : 'offline';
|
|
11602
|
+
}
|
|
11603
|
+
}
|
|
11488
11604
|
return node;
|
|
11489
11605
|
}
|
|
11490
11606
|
function createERNode(erouter) {
|
|
@@ -11509,38 +11625,58 @@ function createFabricRouterNode(fabricRouter, routerType) {
|
|
|
11509
11625
|
nodeOb.routerType = routerType;
|
|
11510
11626
|
return nodeOb;
|
|
11511
11627
|
}
|
|
11628
|
+
// Determines the state of an identity/tunneler <-> edge-router link.
|
|
11629
|
+
// A router-reported problem (offline/errored router) is distinguished from a tunneler-side
|
|
11630
|
+
// problem (enrolled identity that cannot establish a connection to the router, e.g. blocked
|
|
11631
|
+
// by a firewall) so the visualizer can render the two causes differently.
|
|
11512
11632
|
function getEndpointToRouterLinkState(endpointNode, routerNode) {
|
|
11513
|
-
|
|
11514
|
-
|
|
11515
|
-
linkstate = 2; // router is broken — warning
|
|
11633
|
+
if (routerNode.status === 'ERROR' || routerNode.online === 'No') {
|
|
11634
|
+
return { state: 2, reason: 'router-down' }; // router-reported issue
|
|
11516
11635
|
}
|
|
11517
|
-
|
|
11518
|
-
|
|
11519
|
-
|
|
11520
|
-
|
|
11521
|
-
// No session or no connection = offline, not error
|
|
11522
|
-
linkstate = -1;
|
|
11636
|
+
if (endpointNode.status === 'Un-Registered') {
|
|
11637
|
+
// Never enrolled — the path through this identity can't carry traffic, so it's not
|
|
11638
|
+
// "available". Marked unavailable (the node still shows the specific "Not enrolled" cause).
|
|
11639
|
+
return { state: 2, reason: 'misconfigured' };
|
|
11523
11640
|
}
|
|
11524
|
-
|
|
11525
|
-
|
|
11641
|
+
if (endpointNode.apiSession === 'No' || endpointNode.routerConnection === 'No') {
|
|
11642
|
+
// Enrolled but cannot reach the edge router — tunneler-side broken (was previously
|
|
11643
|
+
// shown as a benign "available" link, which is exactly what we're fixing).
|
|
11644
|
+
return { state: 2, reason: 'tunneler-unreachable' };
|
|
11526
11645
|
}
|
|
11527
|
-
|
|
11528
|
-
|
|
11646
|
+
if (routerNode.online === 'Yes' && endpointNode.routerConnection === 'Yes') {
|
|
11647
|
+
return { state: 1, reason: null }; // active/available
|
|
11529
11648
|
}
|
|
11530
|
-
return
|
|
11649
|
+
return { state: -1, reason: null };
|
|
11531
11650
|
}
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
11651
|
+
// Determines the state of a hosting identity/router <-> service link.
|
|
11652
|
+
// hasTerminator is the authoritative signal that the host actually established its hosting
|
|
11653
|
+
// connection: a Bind policy makes a host "expected", but only a terminator proves it connected.
|
|
11654
|
+
function getServiceToEndpointLinkState(hostNode, serviceNode) {
|
|
11655
|
+
const isRouter = !!(hostNode.type && String(hostNode.type).includes('Router'));
|
|
11656
|
+
if (isRouter) {
|
|
11657
|
+
if (hostNode.status === 'ERROR' || hostNode.online === 'No') {
|
|
11658
|
+
return { state: 2, reason: 'router-down' };
|
|
11659
|
+
}
|
|
11660
|
+
return { state: 1, reason: null };
|
|
11661
|
+
}
|
|
11662
|
+
// Identity host (tunneler). Broken-detection uses the host's own connection state, since
|
|
11663
|
+
// terminators can't be attributed to a specific host (see note in getEndpointGraphObj).
|
|
11664
|
+
if (hostNode.status === 'Un-Registered') {
|
|
11665
|
+
return { state: 2, reason: 'misconfigured' }; // never enrolled
|
|
11536
11666
|
}
|
|
11537
|
-
|
|
11538
|
-
return -
|
|
11667
|
+
if (hostNode.apiSession === 'No' || hostNode.routerConnection === 'No') {
|
|
11668
|
+
return { state: 2, reason: 'tunneler-unreachable' }; // enrolled but unreachable (e.g. firewall)
|
|
11539
11669
|
}
|
|
11540
|
-
|
|
11541
|
-
return 1; // active
|
|
11670
|
+
if (hostNode.status === 'Registered' && hostNode.routerConnection === 'Yes') {
|
|
11671
|
+
return { state: 1, reason: null }; // active/available
|
|
11542
11672
|
}
|
|
11543
|
-
return -1;
|
|
11673
|
+
return { state: -1, reason: null };
|
|
11674
|
+
}
|
|
11675
|
+
// Applies a computed LinkState to a link, including its derived weight.
|
|
11676
|
+
function applyLinkState(lnk, ls) {
|
|
11677
|
+
lnk.status = ls.state;
|
|
11678
|
+
lnk.statusReason = ls.reason;
|
|
11679
|
+
lnk.weight = getWeight(ls.state);
|
|
11544
11680
|
}
|
|
11545
11681
|
function getWeight(linkState) {
|
|
11546
11682
|
return linkState === -1 ? 5 : 1;
|
|
@@ -11747,10 +11883,16 @@ class IdentityServicePathComponent {
|
|
|
11747
11883
|
this.isDarkMode = false;
|
|
11748
11884
|
this.refreshRotate = false;
|
|
11749
11885
|
this.closeRotate = true;
|
|
11886
|
+
this.lastRenderContext = null;
|
|
11887
|
+
this.zoomBehavior = null;
|
|
11888
|
+
this.hasInitialFit = false;
|
|
11889
|
+
this.viewBoxWidth = 1050;
|
|
11890
|
+
this.viewBoxHeight = 500;
|
|
11750
11891
|
this.filterText = 'fetching services..';
|
|
11751
11892
|
this.noSearchResults = false;
|
|
11752
11893
|
this.autocompleteOptions = [];
|
|
11753
11894
|
this.fullScreen = false;
|
|
11895
|
+
this.legendCollapsed = false;
|
|
11754
11896
|
this.endpoints = [];
|
|
11755
11897
|
this.selectedService = '';
|
|
11756
11898
|
this.doEndpointHasAppwans = false;
|
|
@@ -11901,6 +12043,8 @@ class IdentityServicePathComponent {
|
|
|
11901
12043
|
this.serviceChanged();
|
|
11902
12044
|
}
|
|
11903
12045
|
serviceChanged() {
|
|
12046
|
+
// Service change implies new topology — re-fit on next render.
|
|
12047
|
+
this.hasInitialFit = false;
|
|
11904
12048
|
this.isLoading = true;
|
|
11905
12049
|
const allPromises = [];
|
|
11906
12050
|
let serviceOb = null;
|
|
@@ -12013,12 +12157,57 @@ class IdentityServicePathComponent {
|
|
|
12013
12157
|
});
|
|
12014
12158
|
}
|
|
12015
12159
|
}
|
|
12160
|
+
this.lastRenderContext = {
|
|
12161
|
+
bindIdnetities,
|
|
12162
|
+
serviceOb,
|
|
12163
|
+
serviceConfigs: service_configs,
|
|
12164
|
+
hostEdgeRouters,
|
|
12165
|
+
};
|
|
12016
12166
|
this.startVisProcess(bindIdnetities, serviceOb, service_configs, hostEdgeRouters, serviceTerminators, serviceCircuits);
|
|
12017
12167
|
this.isLoading = false;
|
|
12018
12168
|
});
|
|
12019
12169
|
});
|
|
12020
12170
|
});
|
|
12021
12171
|
}
|
|
12172
|
+
async refreshFabricData() {
|
|
12173
|
+
if (this.refreshRotate) {
|
|
12174
|
+
return;
|
|
12175
|
+
}
|
|
12176
|
+
this.refreshRotate = true;
|
|
12177
|
+
try {
|
|
12178
|
+
const results = await Promise.allSettled([
|
|
12179
|
+
this.mapDataService.fetchRouters(),
|
|
12180
|
+
this.mapDataService.fetchLinks(),
|
|
12181
|
+
this.mapDataService.fetchCircuits(),
|
|
12182
|
+
this.mapDataService.fetchTerminators(),
|
|
12183
|
+
]);
|
|
12184
|
+
const [routersResult, linksResult, circuitsResult, terminatorsResult] = results;
|
|
12185
|
+
const allFailed = results.every((r) => r.status === 'rejected');
|
|
12186
|
+
this.fabricApiAvailable = !allFailed;
|
|
12187
|
+
if (routersResult.status === 'fulfilled') {
|
|
12188
|
+
this.allRouters = routersResult.value.routers || [];
|
|
12189
|
+
this.routerTypesMap = routersResult.value.routerTypes || new Map();
|
|
12190
|
+
}
|
|
12191
|
+
if (linksResult.status === 'fulfilled') {
|
|
12192
|
+
this.allLinks = linksResult.value || [];
|
|
12193
|
+
}
|
|
12194
|
+
if (circuitsResult.status === 'fulfilled') {
|
|
12195
|
+
this.allCircuits = circuitsResult.value || [];
|
|
12196
|
+
}
|
|
12197
|
+
if (terminatorsResult.status === 'fulfilled') {
|
|
12198
|
+
this.allTerminators = terminatorsResult.value || [];
|
|
12199
|
+
}
|
|
12200
|
+
if (this.lastRenderContext) {
|
|
12201
|
+
const { bindIdnetities, serviceOb, serviceConfigs, hostEdgeRouters } = this.lastRenderContext;
|
|
12202
|
+
const serviceTerminators = this.allTerminators.filter((t) => (t.serviceId || t.service?.id || t.service) === serviceOb.id);
|
|
12203
|
+
const serviceCircuits = this.allCircuits.filter((c) => c.service?.id === serviceOb.id);
|
|
12204
|
+
this.startVisProcess(bindIdnetities, serviceOb, serviceConfigs, hostEdgeRouters, serviceTerminators, serviceCircuits);
|
|
12205
|
+
}
|
|
12206
|
+
}
|
|
12207
|
+
finally {
|
|
12208
|
+
this.refreshRotate = false;
|
|
12209
|
+
}
|
|
12210
|
+
}
|
|
12022
12211
|
addItemToArray(bindIdnetities, ob) {
|
|
12023
12212
|
if (!bindIdnetities.find((item) => item.id === ob.id)) {
|
|
12024
12213
|
bindIdnetities.push(ob);
|
|
@@ -12076,21 +12265,42 @@ class IdentityServicePathComponent {
|
|
|
12076
12265
|
this.dialogRef.close();
|
|
12077
12266
|
}
|
|
12078
12267
|
initTopoView() {
|
|
12079
|
-
const vbWidth =
|
|
12080
|
-
const vbHeight =
|
|
12268
|
+
const vbWidth = this.viewBoxWidth;
|
|
12269
|
+
const vbHeight = this.viewBoxHeight;
|
|
12081
12270
|
const colors = d3.scaleOrdinal(d3.schemeCategory10);
|
|
12082
12271
|
const tooltip = d3.select('#epTooltip');
|
|
12083
|
-
|
|
12084
|
-
|
|
12085
|
-
|
|
12272
|
+
// Hide is deferred so the cursor can travel from the node into the popover (to read/select
|
|
12273
|
+
// its content); entering the popover cancels the hide, leaving it dismisses it.
|
|
12274
|
+
let tooltipHideTimer = null;
|
|
12275
|
+
tooltip.on('mouseenter', () => clearTimeout(tooltipHideTimer));
|
|
12276
|
+
tooltip.on('mouseleave', () => tooltip.style('display', 'none'));
|
|
12277
|
+
const svg = d3.select('#IdentityTopology');
|
|
12278
|
+
const svgEl = svg.node();
|
|
12279
|
+
let node, link;
|
|
12086
12280
|
svg
|
|
12087
12281
|
.attr('width', '100%')
|
|
12088
12282
|
.attr('height', '100%')
|
|
12089
|
-
// .call(zoom)
|
|
12090
12283
|
.attr('viewBox', '-20 -20 ' + (vbWidth + 40) + ' ' + vbHeight)
|
|
12091
|
-
.attr('preserveAspectRatio', 'xMidYMin meet')
|
|
12092
|
-
|
|
12284
|
+
.attr('preserveAspectRatio', 'xMidYMin meet');
|
|
12285
|
+
// Capture any pre-existing zoom transform (from previous render / user gestures)
|
|
12286
|
+
// so we can re-apply it after recreating the content group.
|
|
12287
|
+
const existingTransform = svgEl
|
|
12288
|
+
? d3.zoomTransform(svgEl)
|
|
12289
|
+
: d3.zoomIdentity;
|
|
12093
12290
|
svg.selectAll('g').remove();
|
|
12291
|
+
// Wrap all topology content in a <g> so zoom transforms have something to translate.
|
|
12292
|
+
const contentG = svg.append('g').attr('class', 'topology-content');
|
|
12293
|
+
contentG.attr('transform', existingTransform.toString());
|
|
12294
|
+
const handleZoom = (e) => {
|
|
12295
|
+
contentG.attr('transform', e.transform);
|
|
12296
|
+
};
|
|
12297
|
+
if (!this.zoomBehavior) {
|
|
12298
|
+
this.zoomBehavior = d3.zoom().scaleExtent([0.1, 5]).on('zoom', handleZoom);
|
|
12299
|
+
}
|
|
12300
|
+
else {
|
|
12301
|
+
this.zoomBehavior.on('zoom', handleZoom);
|
|
12302
|
+
}
|
|
12303
|
+
svg.call(this.zoomBehavior);
|
|
12094
12304
|
const simulation = d3
|
|
12095
12305
|
.forceSimulation()
|
|
12096
12306
|
.force('link', d3
|
|
@@ -12104,47 +12314,49 @@ class IdentityServicePathComponent {
|
|
|
12104
12314
|
.force('center', d3.forceCenter(vbWidth / 2, vbHeight / 2));
|
|
12105
12315
|
const links = this.graphJsonObj.links;
|
|
12106
12316
|
const nodes = this.graphJsonObj.nodes;
|
|
12107
|
-
link =
|
|
12317
|
+
link = contentG
|
|
12108
12318
|
.selectAll('g.line.line')
|
|
12109
12319
|
.data(links)
|
|
12110
12320
|
.enter()
|
|
12111
12321
|
.append('g')
|
|
12112
12322
|
.attr('class', 'link')
|
|
12113
12323
|
.attr('marker-end', 'url(#arrowhead)');
|
|
12324
|
+
// Three link styles, distinguishable by pattern (color-blind-safe) and color. A single
|
|
12325
|
+
// "broken" style covers every failure — which END is the problem is read from the node
|
|
12326
|
+
// colors (an offline router vs an unreachable/un-enrolled endpoint), so the link needn't
|
|
12327
|
+
// re-encode the cause:
|
|
12328
|
+
// active circuit -> green solid
|
|
12329
|
+
// available -> gray dashed
|
|
12330
|
+
// broken -> red dotted
|
|
12331
|
+
const isBroken = (d) => d.status === 2;
|
|
12114
12332
|
link
|
|
12115
12333
|
.append('line')
|
|
12116
12334
|
.style('stroke-width', function (d) {
|
|
12117
|
-
if (d.linkType === 'active-circuit')
|
|
12335
|
+
if (d.linkType === 'active-circuit')
|
|
12118
12336
|
return 1.5;
|
|
12119
|
-
|
|
12337
|
+
if (isBroken(d))
|
|
12338
|
+
return 2.25;
|
|
12120
12339
|
return 1.25;
|
|
12340
|
+
})
|
|
12341
|
+
.style('stroke-linecap', function (d) {
|
|
12342
|
+
// Rounded caps turn the tight broken dash array into actual dots.
|
|
12343
|
+
return isBroken(d) ? 'round' : 'butt';
|
|
12121
12344
|
})
|
|
12122
12345
|
.style('stroke', function (d) {
|
|
12123
|
-
if (d.linkType === 'active-circuit')
|
|
12346
|
+
if (d.linkType === 'active-circuit')
|
|
12124
12347
|
return '#00cd13';
|
|
12125
|
-
|
|
12348
|
+
if (isBroken(d))
|
|
12349
|
+
return '#e6432e';
|
|
12126
12350
|
return '#8a8f9a';
|
|
12127
12351
|
})
|
|
12128
12352
|
.style('stroke-dasharray', function (d) {
|
|
12129
|
-
if (d.linkType === 'active-circuit')
|
|
12130
|
-
return null;
|
|
12131
|
-
|
|
12132
|
-
|
|
12133
|
-
|
|
12134
|
-
link.each(function (d, i) {
|
|
12135
|
-
const _this = d3.select(this);
|
|
12136
|
-
if (d.status === 2) {
|
|
12137
|
-
// Warning/misconfigured — show link-cut icon
|
|
12138
|
-
_this
|
|
12139
|
-
.append('image')
|
|
12140
|
-
.attr('xlink:href', function () {
|
|
12141
|
-
return 'assets/images/visualizer/link-cut.png';
|
|
12142
|
-
})
|
|
12143
|
-
.attr('width', '20')
|
|
12144
|
-
.attr('height', '24');
|
|
12145
|
-
}
|
|
12353
|
+
if (d.linkType === 'active-circuit')
|
|
12354
|
+
return null; // solid
|
|
12355
|
+
if (isBroken(d))
|
|
12356
|
+
return '0.5,6'; // dotted
|
|
12357
|
+
return '5,5'; // available: dashed
|
|
12146
12358
|
});
|
|
12147
|
-
node =
|
|
12359
|
+
node = contentG
|
|
12148
12360
|
.selectAll('.node')
|
|
12149
12361
|
.data(nodes)
|
|
12150
12362
|
.enter()
|
|
@@ -12154,61 +12366,73 @@ class IdentityServicePathComponent {
|
|
|
12154
12366
|
.append('image')
|
|
12155
12367
|
.attr('xlink:href', function (d) {
|
|
12156
12368
|
const routerImagePath = 'assets/images/visualizer/Routers.png';
|
|
12157
|
-
const nonProvisionedRouterImagePath = 'assets/images/visualizer/ER.png';
|
|
12158
|
-
const errRouterImagePath = 'assets/images/visualizer/error_er.png';
|
|
12159
|
-
const errEndpointImagePath = 'assets/images/visualizer/host-error.png';
|
|
12160
12369
|
const unregisteredEndpointImagePath = 'assets/images/visualizer/host_unregistered.png';
|
|
12161
12370
|
const endPointPath = 'assets/images/visualizer/host.png';
|
|
12162
|
-
if (d.type.includes('Identity')
|
|
12163
|
-
|
|
12164
|
-
|
|
12165
|
-
|
|
12166
|
-
|
|
12167
|
-
|
|
12168
|
-
else if (d.type.includes('Identity')) {
|
|
12371
|
+
if (d.type.includes('Identity')) {
|
|
12372
|
+
// Un-enrolled identities get the distinct "unregistered" icon; otherwise the normal
|
|
12373
|
+
// host icon — the status dot below conveys online/offline/unknown.
|
|
12374
|
+
if (d.connState === 'unenrolled' || d.status === 'Un-Registered') {
|
|
12375
|
+
return unregisteredEndpointImagePath;
|
|
12376
|
+
}
|
|
12169
12377
|
return endPointPath;
|
|
12170
12378
|
}
|
|
12171
|
-
|
|
12172
|
-
|
|
12173
|
-
|
|
12174
|
-
}
|
|
12175
|
-
else if (d.type.includes('Router') && d.status === 'PROVISIONED') {
|
|
12379
|
+
if (d.type.includes('Router')) {
|
|
12380
|
+
// One base router icon; the status dot below carries online/offline (the baked dot in
|
|
12381
|
+
// the PNG is covered by that overlay).
|
|
12176
12382
|
return routerImagePath;
|
|
12177
12383
|
}
|
|
12178
|
-
else if (d.type.includes('Router')) {
|
|
12179
|
-
return nonProvisionedRouterImagePath;
|
|
12180
|
-
}
|
|
12181
12384
|
if (d.group === '4') {
|
|
12182
12385
|
return 'assets/images/visualizer/service.png';
|
|
12183
12386
|
}
|
|
12184
|
-
|
|
12185
|
-
return '';
|
|
12186
|
-
}
|
|
12387
|
+
return '';
|
|
12187
12388
|
})
|
|
12188
12389
|
.attr('x', -25)
|
|
12189
12390
|
.attr('y', -25)
|
|
12190
12391
|
.attr('width', '40')
|
|
12191
12392
|
.attr('height', '40');
|
|
12192
|
-
//
|
|
12193
|
-
|
|
12393
|
+
// Status dot for identities AND routers. Colors a single dot by the node's real state
|
|
12394
|
+
// (no "error" — that isn't a value the API exposes). Routers use a larger dot positioned to
|
|
12395
|
+
// cover the status dot baked into the router PNG.
|
|
12396
|
+
const statusColor = (d) => {
|
|
12397
|
+
if (d.type.includes('Router')) {
|
|
12398
|
+
return (d.online === 'No' || d.status === 'ERROR') ? '#8a8f9a' : '#08dc5a';
|
|
12399
|
+
}
|
|
12400
|
+
switch (d.connState) {
|
|
12401
|
+
case 'online': return '#08dc5a'; // green
|
|
12402
|
+
case 'unknown': return '#e6a700'; // amber — controller can't determine
|
|
12403
|
+
case 'unenrolled': return '#ffffff'; // hollow (gray ring below)
|
|
12404
|
+
case 'offline':
|
|
12405
|
+
default: return '#8a8f9a'; // gray
|
|
12406
|
+
}
|
|
12407
|
+
};
|
|
12408
|
+
// Positioned to sit directly over the dot baked into each icon PNG (≈ the icon's top-right),
|
|
12409
|
+
// sized just large enough to cover it (the white halo masks the baked dot's soft glow).
|
|
12410
|
+
node.filter((d) => d.type.includes('Identity') || d.type.includes('Router'))
|
|
12194
12411
|
.append('circle')
|
|
12195
|
-
.attr('cx',
|
|
12196
|
-
.attr('cy', -20)
|
|
12197
|
-
.attr('r',
|
|
12198
|
-
.attr('fill',
|
|
12199
|
-
|
|
12200
|
-
|
|
12201
|
-
|
|
12202
|
-
|
|
12203
|
-
|
|
12204
|
-
|
|
12205
|
-
//
|
|
12412
|
+
.attr('cx', (d) => d.type.includes('Router') ? 6 : 8)
|
|
12413
|
+
.attr('cy', (d) => d.type.includes('Router') ? -20 : -19)
|
|
12414
|
+
.attr('r', 5)
|
|
12415
|
+
.attr('fill', (d) => statusColor(d))
|
|
12416
|
+
// Un-enrolled identities render as a hollow ring (gray border); everything else gets a
|
|
12417
|
+
// white separator ring against the icon.
|
|
12418
|
+
.attr('stroke', (d) => (!d.type.includes('Router') && d.connState === 'unenrolled') ? '#8a8f9a' : 'white')
|
|
12419
|
+
.attr('stroke-width', 2);
|
|
12420
|
+
// Node labels: for compound pipe-delimited names ("<networkId>|<identityId>|Human Name")
|
|
12421
|
+
// show only the last segment so the canvas stays readable; the full name remains in the
|
|
12422
|
+
// hover tooltip.
|
|
12423
|
+
const getDisplayLabel = (fullName) => {
|
|
12424
|
+
if (!fullName)
|
|
12425
|
+
return '';
|
|
12426
|
+
const segments = fullName.split('|').map((s) => s.trim()).filter((s) => s.length > 0);
|
|
12427
|
+
const candidate = segments.length > 0 ? segments[segments.length - 1] : fullName;
|
|
12428
|
+
const maxLen = 28;
|
|
12429
|
+
return candidate.length > maxLen
|
|
12430
|
+
? candidate.substring(0, maxLen) + '…'
|
|
12431
|
+
: candidate;
|
|
12432
|
+
};
|
|
12206
12433
|
node.each(function (d) {
|
|
12207
12434
|
const g = d3.select(this);
|
|
12208
|
-
const
|
|
12209
|
-
const displayName = d.name && d.name.length > maxLen
|
|
12210
|
-
? d.name.substring(0, maxLen) + '\u2026'
|
|
12211
|
-
: d.name;
|
|
12435
|
+
const displayName = getDisplayLabel(d.name);
|
|
12212
12436
|
const text = g.append('text')
|
|
12213
12437
|
.attr('x', 0)
|
|
12214
12438
|
.attr('y', 28)
|
|
@@ -12311,10 +12535,13 @@ class IdentityServicePathComponent {
|
|
|
12311
12535
|
} // end of ticked
|
|
12312
12536
|
function removeTooltip() {
|
|
12313
12537
|
if (tooltip) {
|
|
12314
|
-
|
|
12538
|
+
// Defer the hide so the user can move onto the popover; mouseenter (above) cancels it.
|
|
12539
|
+
clearTimeout(tooltipHideTimer);
|
|
12540
|
+
tooltipHideTimer = setTimeout(() => tooltip.style('display', 'none'), 220);
|
|
12315
12541
|
}
|
|
12316
12542
|
}
|
|
12317
12543
|
function drawTooltip(event, d, endpointUtlizationJson) {
|
|
12544
|
+
clearTimeout(tooltipHideTimer);
|
|
12318
12545
|
document.getElementById('epTooltip').innerHTML = readKeyValues(d, endpointUtlizationJson);
|
|
12319
12546
|
tooltip.style('display', 'block');
|
|
12320
12547
|
tooltip.style('white-space', 'pre-wrap');
|
|
@@ -12323,34 +12550,133 @@ class IdentityServicePathComponent {
|
|
|
12323
12550
|
tooltip.style('left', x);
|
|
12324
12551
|
tooltip.style('top', y);
|
|
12325
12552
|
}
|
|
12553
|
+
// Builds a compact status "card" for a node's hover tooltip: a bold name, a prominent
|
|
12554
|
+
// plain-language status line (with cause), and a few key facts. Makes the connectivity of
|
|
12555
|
+
// customer-hosted tunnelers and routers immediately legible.
|
|
12326
12556
|
function readKeyValues(d, endpointUtlizationJson) {
|
|
12327
|
-
const
|
|
12328
|
-
|
|
12329
|
-
|
|
12330
|
-
|
|
12331
|
-
|
|
12332
|
-
|
|
12333
|
-
//
|
|
12334
|
-
|
|
12335
|
-
|
|
12336
|
-
|
|
12337
|
-
|
|
12338
|
-
|
|
12339
|
-
|
|
12340
|
-
|
|
12341
|
-
|
|
12342
|
-
if (
|
|
12343
|
-
|
|
12344
|
-
|
|
12345
|
-
|
|
12346
|
-
|
|
12557
|
+
const esc = (s) => String(s).replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c]));
|
|
12558
|
+
const isRouter = !!(d.type && String(d.type).includes('Router'));
|
|
12559
|
+
const isService = d.group === '4' || !!(d.type && String(d.type).includes('Service'));
|
|
12560
|
+
// The status band is the single source of truth for connectivity/enrollment — it replaces
|
|
12561
|
+
// any separate "Enrolled" row. `caption` is a short fragment, used only where it tells the
|
|
12562
|
+
// operator something the label alone doesn't (i.e. what to go check).
|
|
12563
|
+
// Status pill mirrors the node dot — the component's real state (online / offline /
|
|
12564
|
+
// unknown / not-enrolled), never a synthesized "error". Path problems live on the links.
|
|
12565
|
+
let cls = 'neutral';
|
|
12566
|
+
let label = '';
|
|
12567
|
+
if (isService) {
|
|
12568
|
+
label = 'Service';
|
|
12569
|
+
}
|
|
12570
|
+
else if (isRouter) {
|
|
12571
|
+
// Routers only expose isOnline (no edgeRouterConnectionStatus).
|
|
12572
|
+
if (d.online === 'No' || d.status === 'ERROR') {
|
|
12573
|
+
cls = 'offline';
|
|
12574
|
+
label = 'Offline';
|
|
12575
|
+
}
|
|
12576
|
+
else {
|
|
12577
|
+
cls = 'ok';
|
|
12578
|
+
label = 'Online';
|
|
12347
12579
|
}
|
|
12348
|
-
|
|
12349
|
-
|
|
12580
|
+
}
|
|
12581
|
+
else {
|
|
12582
|
+
switch (d.connState) {
|
|
12583
|
+
case 'online':
|
|
12584
|
+
cls = 'ok';
|
|
12585
|
+
label = 'Online';
|
|
12586
|
+
break;
|
|
12587
|
+
case 'unknown':
|
|
12588
|
+
cls = 'unknown';
|
|
12589
|
+
label = 'Unknown';
|
|
12590
|
+
break;
|
|
12591
|
+
case 'unenrolled':
|
|
12592
|
+
cls = 'offline';
|
|
12593
|
+
label = 'Not enrolled';
|
|
12594
|
+
break;
|
|
12595
|
+
case 'offline':
|
|
12596
|
+
default:
|
|
12597
|
+
cls = 'offline';
|
|
12598
|
+
label = 'Offline';
|
|
12599
|
+
break;
|
|
12350
12600
|
}
|
|
12351
|
-
|
|
12352
|
-
|
|
12353
|
-
|
|
12601
|
+
}
|
|
12602
|
+
// No role for the service node — the icon/name already make it obvious, and a "Role: Service"
|
|
12603
|
+
// row would just repeat itself.
|
|
12604
|
+
// Role is the policy action (Dial/Bind) the identity has for the service — NOT the host
|
|
12605
|
+
// mechanism, which we can't know (tunneler vs SDK vs router all look the same here).
|
|
12606
|
+
let role = '';
|
|
12607
|
+
if (d.group === '1')
|
|
12608
|
+
role = 'Client (Dial)';
|
|
12609
|
+
else if (d.group === '3')
|
|
12610
|
+
role = 'Host (Bind)';
|
|
12611
|
+
else if (isRouter)
|
|
12612
|
+
role = d.routerType === 'transit-router' ? 'Transit router' : 'Edge router';
|
|
12613
|
+
// Facts that add detail beyond the status pill. The pill summarizes overall state; these
|
|
12614
|
+
// are the granular signals an operator uses to diagnose it (a host can have an API session
|
|
12615
|
+
// yet no router connection). Only the pill's own inputs (Online/Type/Enrolled) are omitted.
|
|
12616
|
+
// Rows are [label, value, optional hover-context]. Service rows are purpose-labeled and
|
|
12617
|
+
// adapt to how the service is configured (intercept+host, host-only, intercept-only, or
|
|
12618
|
+
// SDK-defined with no address config).
|
|
12619
|
+
const rows = [];
|
|
12620
|
+
if (isService) {
|
|
12621
|
+
if (d.interceptSummary) {
|
|
12622
|
+
rows.push(['Intercept', d.interceptSummary]);
|
|
12623
|
+
}
|
|
12624
|
+
if (d.hostSummary) {
|
|
12625
|
+
rows.push(['Host', d.hostSummary]);
|
|
12626
|
+
}
|
|
12627
|
+
if (!d.interceptSummary && !d.hostSummary) {
|
|
12628
|
+
// No intercept/host config. State the facts (the config types present, or none) rather
|
|
12629
|
+
// than inferring "SDK-defined" — there's no property that actually tells us the host type.
|
|
12630
|
+
rows.push(['Config types',
|
|
12631
|
+
(d.configTypeNames && d.configTypeNames.length) ? d.configTypeNames.join(', ') : 'None']);
|
|
12632
|
+
}
|
|
12633
|
+
if (typeof d.encryptionRequired === 'boolean') {
|
|
12634
|
+
rows.push(['Encryption', d.encryptionRequired ? 'Required' : 'Not required']);
|
|
12635
|
+
}
|
|
12636
|
+
if (d.terminatorStrategy) {
|
|
12637
|
+
rows.push(['Terminators', d.terminatorStrategy]);
|
|
12638
|
+
}
|
|
12639
|
+
if (d.serviceRoleAttributes && d.serviceRoleAttributes.length) {
|
|
12640
|
+
rows.push(['Attributes', d.serviceRoleAttributes.join(', ')]);
|
|
12641
|
+
}
|
|
12642
|
+
if (d.permissions && d.permissions.length) {
|
|
12643
|
+
rows.push(['Your access', d.permissions.join(', ')]);
|
|
12644
|
+
}
|
|
12645
|
+
}
|
|
12646
|
+
else {
|
|
12647
|
+
if (role)
|
|
12648
|
+
rows.push(['Role', role]);
|
|
12649
|
+
if (isRouter) {
|
|
12650
|
+
if (d.tunnelerEnabled === 'Yes' || d.tunnelerEnabled === 'No') {
|
|
12651
|
+
rows.push(['Tunneler Enabled', d.tunnelerEnabled]);
|
|
12652
|
+
}
|
|
12653
|
+
}
|
|
12654
|
+
else {
|
|
12655
|
+
if (d.routerConnection === 'Yes' || d.routerConnection === 'No') {
|
|
12656
|
+
rows.push(['Router Connection', d.routerConnection]);
|
|
12657
|
+
}
|
|
12658
|
+
if (d.apiSession === 'Yes' || d.apiSession === 'No') {
|
|
12659
|
+
rows.push(['API Session', d.apiSession]);
|
|
12660
|
+
}
|
|
12661
|
+
if (d.os)
|
|
12662
|
+
rows.push(['OS', d.os]);
|
|
12663
|
+
if (d.mfaEnabled === true)
|
|
12664
|
+
rows.push(['MFA', 'Enabled']);
|
|
12665
|
+
}
|
|
12666
|
+
}
|
|
12667
|
+
let info = "<div class='tool-tip-container tooltip-card'>";
|
|
12668
|
+
info += '<div class="tooltip-header">' + esc(d.name || '') + '</div>';
|
|
12669
|
+
// The service node carries no connectivity state, so it gets no status pill.
|
|
12670
|
+
if (!isService) {
|
|
12671
|
+
info += '<div class="tt-status-pill tt-' + cls + '">'
|
|
12672
|
+
+ '<span class="tt-status-dot"></span>'
|
|
12673
|
+
+ '<span class="tt-status-label">' + esc(label) + '</span>'
|
|
12674
|
+
+ '</div>';
|
|
12675
|
+
}
|
|
12676
|
+
rows.forEach(([k, v, t]) => {
|
|
12677
|
+
info += '<div class="prop-row"' + (t ? ' title="' + esc(t) + '"' : '') + '>'
|
|
12678
|
+
+ '<span class="prop-label">' + esc(k) + '</span>'
|
|
12679
|
+
+ '<span class="prop-value">' + esc(v) + '</span>'
|
|
12354
12680
|
+ '</div>';
|
|
12355
12681
|
});
|
|
12356
12682
|
return info + '</div>';
|
|
@@ -12359,13 +12685,79 @@ class IdentityServicePathComponent {
|
|
|
12359
12685
|
return o instanceof Object && o.constructor === Object;
|
|
12360
12686
|
}
|
|
12361
12687
|
this.refreshRotate = false;
|
|
12688
|
+
// After the first render only, fit the graph to the viewBox so big topologies don't
|
|
12689
|
+
// overflow off-screen. Subsequent renders (refresh ticks) preserve the user's transform.
|
|
12690
|
+
if (!this.hasInitialFit) {
|
|
12691
|
+
setTimeout(() => {
|
|
12692
|
+
this.fitToContent();
|
|
12693
|
+
this.hasInitialFit = true;
|
|
12694
|
+
}, 0);
|
|
12695
|
+
}
|
|
12362
12696
|
} // end of initTopoView
|
|
12697
|
+
fitToContent() {
|
|
12698
|
+
if (!this.zoomBehavior)
|
|
12699
|
+
return;
|
|
12700
|
+
const nodes = this.graphJsonObj?.nodes || [];
|
|
12701
|
+
if (nodes.length === 0)
|
|
12702
|
+
return;
|
|
12703
|
+
const xs = nodes
|
|
12704
|
+
.map((n) => n.posx)
|
|
12705
|
+
.filter((v) => typeof v === 'number' && !isNaN(v));
|
|
12706
|
+
const ys = nodes
|
|
12707
|
+
.map((n) => n.posy)
|
|
12708
|
+
.filter((v) => typeof v === 'number' && !isNaN(v));
|
|
12709
|
+
if (xs.length === 0 || ys.length === 0)
|
|
12710
|
+
return;
|
|
12711
|
+
const padding = 60;
|
|
12712
|
+
const minX = Math.min(...xs);
|
|
12713
|
+
const maxX = Math.max(...xs);
|
|
12714
|
+
const minY = Math.min(...ys);
|
|
12715
|
+
const maxY = Math.max(...ys);
|
|
12716
|
+
const contentWidth = (maxX - minX) + padding * 2;
|
|
12717
|
+
const contentHeight = (maxY - minY) + padding * 2;
|
|
12718
|
+
if (contentWidth <= 0 || contentHeight <= 0)
|
|
12719
|
+
return;
|
|
12720
|
+
// Reserve room in the bottom-right for the legend so the auto-fit doesn't place nodes
|
|
12721
|
+
// underneath it. When the legend is collapsed there's nothing to avoid.
|
|
12722
|
+
const reserveRight = this.legendCollapsed ? 0 : 160;
|
|
12723
|
+
const reserveBottom = this.legendCollapsed ? 0 : 90;
|
|
12724
|
+
const availWidth = this.viewBoxWidth - reserveRight;
|
|
12725
|
+
const availHeight = this.viewBoxHeight - reserveBottom;
|
|
12726
|
+
const scale = Math.min(availWidth / contentWidth, availHeight / contentHeight, 1);
|
|
12727
|
+
// Center the content within the available (legend-free) area, which sits at the top-left,
|
|
12728
|
+
// leaving the reserved margin on the right/bottom for the legend panel.
|
|
12729
|
+
const tx = (availWidth - contentWidth * scale) / 2 - (minX - padding) * scale;
|
|
12730
|
+
const ty = (availHeight - contentHeight * scale) / 2 - (minY - padding) * scale;
|
|
12731
|
+
d3.select('#IdentityTopology')
|
|
12732
|
+
.transition()
|
|
12733
|
+
.duration(300)
|
|
12734
|
+
.call(this.zoomBehavior.transform, d3.zoomIdentity.translate(tx, ty).scale(scale));
|
|
12735
|
+
}
|
|
12736
|
+
zoomIn() {
|
|
12737
|
+
if (!this.zoomBehavior)
|
|
12738
|
+
return;
|
|
12739
|
+
d3.select('#IdentityTopology')
|
|
12740
|
+
.transition()
|
|
12741
|
+
.duration(200)
|
|
12742
|
+
.call(this.zoomBehavior.scaleBy, 1.4);
|
|
12743
|
+
}
|
|
12744
|
+
zoomOut() {
|
|
12745
|
+
if (!this.zoomBehavior)
|
|
12746
|
+
return;
|
|
12747
|
+
d3.select('#IdentityTopology')
|
|
12748
|
+
.transition()
|
|
12749
|
+
.duration(200)
|
|
12750
|
+
.call(this.zoomBehavior.scaleBy, 1 / 1.4);
|
|
12751
|
+
}
|
|
12752
|
+
resetView() {
|
|
12753
|
+
this.fitToContent();
|
|
12754
|
+
}
|
|
12363
12755
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: IdentityServicePathComponent, deps: [{ token: IdentityServicePathHelper }, { token: MapDataService }, { token: SETTINGS_SERVICE }, { token: ZITI_DATA_SERVICE }, { token: i4.Router }, { token: i5.MatDialogRef }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
12364
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: IdentityServicePathComponent, isStandalone: false, selector: "app-identity-visualizer", ngImport: i0, template: "<div [ngClass]=\"{ 'full-screen': fullScreen }\" class=\"modal solid\" id=\"IdentityVisualizer\">\n <div class=\"tp-data-header\">\n <div class=\"modaltitle\">Identity Service Path</div>\n <div (click)=\"hide()\" class=\"buttonBall icon-close\" id=\"EscZEndpointClose\"></div>\n <div (click)=\"toggleModalSize()\" class=\"buttonBall resize\" id=\"ResizeModalButton\"></div>\n </div>\n <div [ngClass]=\"isDarkMode==true?'darkMode':'lightMode'\" class=\"identity-topology-container\">\n <div style=\"width: 100%; height: 80px; float: left\">\n <div class=\"ep-topology-filter-container listBox row\">\n <input\n (keyup)=\"filterSearchArray()\"\n [(ngModel)]=\"selectedService\"\n [matAutocomplete]=\"auto\"\n class=\"topology-filter-input\"\n id=\"filterText\"\n matInput\n placeholder=\"Type to search identity accessible services.\"\n />\n\n @if (filterText) {\n <div\n (click)=\"clearSearchFilter()\"\n class=\"clear-svc-icon icon-close\"\n id=\"ClearSearchFilter\"\n ></div>\n }\n <div [hidden]=\"!noSearchResults\" class=\"no-results-warning\">No Results Found</div>\n <mat-autocomplete #auto=\"matAutocomplete\" (optionSelected)=\"autocompleteSearch($event)\">\n @for (option of autocompleteOptions; track option) {\n <mat-option [value]=\"option.name\">\n {{ option.name }}\n </mat-option>\n }\n </mat-autocomplete>\n </div>\n </div>\n\n <div class=\"topotip\" id=\"epTooltip\"></div>\n\n <svg style='pointer-events:all' height=\"1000\" id=\"IdentityTopology\" width=\"1400\">\n </svg>\n @if (isLoading) {\n <lib-loading-indicator [isLoading]=\"isLoading\"></lib-loading-indicator>\n }\n\n <div class=\"topology-legend\">\n <div class=\"legend-section\">\n <div class=\"legend-section-title\">Nodes</div>\n <div class=\"legend-item\">\n <div class=\"legend-icon-col\"><img class=\"legend-node-icon\" src=\"assets/images/visualizer/host.png\" /></div>\n <div class=\"legend-text\">Identity</div>\n </div>\n <div class=\"legend-item\">\n <div class=\"legend-icon-col\"><img class=\"legend-node-icon\" src=\"assets/images/visualizer/Routers.png\" /></div>\n <div class=\"legend-text\">Edge Router</div>\n </div>\n <div class=\"legend-item\">\n <div class=\"legend-icon-col\"><img class=\"legend-node-icon\" src=\"assets/images/visualizer/service.png\" /></div>\n <div class=\"legend-text\">Service</div>\n </div>\n </div>\n <div class=\"legend-section\">\n <div class=\"legend-section-title\">Status</div>\n <div class=\"legend-item\">\n <div class=\"legend-icon-col\"><div class=\"legend-circle registered_in_use\"></div></div>\n <div class=\"legend-text\">Online</div>\n </div>\n <div class=\"legend-item\">\n <div class=\"legend-icon-col\"><div class=\"legend-circle unregistered\"></div></div>\n <div class=\"legend-text\">Offline</div>\n </div>\n <div class=\"legend-item\">\n <div class=\"legend-icon-col\"><div class=\"legend-circle errored\"></div></div>\n <div class=\"legend-text\">Error</div>\n </div>\n </div>\n @if (fabricApiAvailable) {\n <div class=\"legend-section\">\n <div class=\"legend-section-title\">Path</div>\n <div class=\"legend-item\">\n <div class=\"legend-icon-col\"><div class=\"legend-line active-circuit\"></div></div>\n <div class=\"legend-text\">Active Circuit</div>\n </div>\n <div class=\"legend-item\">\n <div class=\"legend-icon-col\"><div class=\"legend-line available\"></div></div>\n <div class=\"legend-text\">Available</div>\n </div>\n <div class=\"legend-item\">\n <div class=\"legend-icon-col\"><div class=\"legend-icon misconfigured\"></div></div>\n <div class=\"legend-text\">Broken</div>\n </div>\n </div>\n }\n </div>\n\n </div>\n</div>\n", styles: ["#IdentityVisualizer{overflow:hidden;width:75%;height:85%;background-color:var(--background);border-radius:1.5625rem;z-index:1001;margin:auto;inset:0rem;position:fixed;display:flex;flex-direction:column}#IdentityVisualizer .buttonBall{cursor:pointer;font-weight:600}#IdentityVisualizer .buttonBall.icon-close{position:absolute;top:1.25rem;right:1.25rem;font-size:1.4375rem;color:var(--icon)}#IdentityVisualizer .buttonBall:hover{filter:brightness(50%)}#IdentityVisualizer .clear-svc-icon{font-size:1.4375rem;font-weight:600;position:relative;top:.9375rem;right:2.5rem;color:var(--icon);cursor:pointer}#IdentityVisualizer .clear-svc-icon:hover{filter:brightness(50%)}#IdentityVisualizer.full-screen{width:100%;height:100%;border-radius:0}.ep-topology-filter-container{width:37%;display:flex;flex-direction:row;position:absolute;margin:1.25rem;padding:0}.buttonBall.resize{top:1.25rem;width:1.4375rem;height:1.4375rem;right:3.4375rem;background-image:url(/assets/svgs/Resize.svg);background-size:1.4375rem 1.4375rem;position:absolute;cursor:pointer}.identity-topology-container{display:flex;margin-left:-.3125rem;margin-top:.625rem;flex-direction:column;cursor:grab;flex:1;overflow:hidden}.lightMode{background:#f5e9f2;background-image:linear-gradient(#ecd3e6 .125rem,transparent 0),linear-gradient(90deg,#ecd3e6 .125rem,transparent 0),linear-gradient(#ecd3e6 .0625rem,transparent 0),linear-gradient(90deg,#ecd3e6 .0625rem,transparent 0);background-size:2.5rem 2.5rem,2.5rem 2.5rem,.625rem .625rem,.625rem .625rem}.darkMode{background:#000a17;background-image:linear-gradient(#001127 .125rem,transparent 0),linear-gradient(90deg,#001127 .125rem,transparent 0),linear-gradient(#001127 1px,transparent 0),linear-gradient(90deg,#001127 1px,transparent 0);background-size:2.5rem 2.5rem,2.5rem 2.5rem,.625rem .625rem,.625rem .625rem}.tp-data-header{height:2.5rem;font-size:1.5625rem;font-weight:600;margin-left:1.25rem;margin-top:.625rem;margin-bottom:.3125rem;font-family:Russo One;color:var(--primaryColor);font-size:1.875rem;text-transform:uppercase;font-weight:400}.node text{font:.75rem Open Sans,Arial,sans-serif}.modaltitle{padding-top:.375rem;padding-left:.3125rem;color:var(--primaryColor)}.modalbuttons{padding-right:1.25rem}.listBox.row .close{position:fixed;display:flex;flex-direction:column;font-family:icomoon!important;font-style:normal;font-weight:600;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--icon);text-align:center;font-size:2.1875rem;line-height:2.5rem;left:35%;top:17%;width:2.5rem;height:2.5rem;background-image:url(/assets/svgs/close.svg);z-index:600;background-position:center center;background-size:75%;cursor:pointer;transition:.3s;justify-content:end;align-items:center}.tp-topo-data-header{border:0rem solid black;height:75rem;font-size:.75rem;font-weight:400;margin-left:.75rem;margin-top:.1875rem;color:var(--primaryColor)}.services-select{width:25rem;flex-shrink:0;border-radius:1.6875rem;height:3.1875rem;margin-left:1.375rem;margin-top:.625rem;background-color:#fff;border-color:var(--navigation);box-shadow:0 1.25rem 1.4375rem 0 var(--shadow);color:var(--tableText);padding-left:1.25rem}select{background-image:url(/assets/svgs/ArrowDown.svg);background-position:center right .9375rem;background-repeat:no-repeat;background-size:1.125rem;padding-right:2.9375rem}.topology-filter-input{height:3.125rem;width:95%;border-radius:1.5625rem;border-color:var(--navigation);background-color:var(--navigation);box-shadow:0 1.25rem 1.4375rem 0 var(--shadow);padding-left:1.25rem}::ng-deep .topotip{display:none;position:fixed;background:var(--navigation);border-radius:.5rem;pointer-events:none;box-shadow:0 .25rem 1rem 0 var(--shadow);color:var(--tableText);z-index:2000;padding:0;max-width:24rem;overflow:hidden}.topology-legend{width:13rem;border-radius:.5rem;position:absolute;bottom:1rem;right:.625rem;background:var(--navigation);box-shadow:0 .25rem .75rem 0 var(--shadow);padding:.9rem}.legend-section{padding:.3rem 0}.legend-section:first-child{padding-top:0}.legend-section:last-child{padding-bottom:0;margin-bottom:-.5rem}.legend-section:not(:last-child){border-bottom:1px solid var(--shadow)}.legend-section-title{font-family:Russo One,sans-serif;font-size:.9rem;text-transform:uppercase;letter-spacing:.05em;color:var(--primary);margin-bottom:.25rem}.legend-item{display:flex;align-items:center;gap:0;padding:.225rem 0}.legend-icon-col{width:2.5rem;min-width:2.5rem;display:flex;align-items:center;justify-content:center;margin-right:.4rem}.legend-circle{height:.75rem;width:.75rem;min-width:.75rem;border-radius:50%}.legend-circle.unregistered{background-color:#8a8f9a}.legend-circle.registered_in_use{background-color:#08dc5a}.legend-circle.errored{background-color:#fb0d3d}.legend-line{height:.225rem;width:1.5rem}.legend-line.active-circuit{background-color:#00cd13}.legend-line.available{background:repeating-linear-gradient(to right,#8a8f9a 0,#8a8f9a .25rem,transparent .25rem,transparent .5rem)}.legend-text{font-size:.975rem;font-weight:500;color:var(--tableText);white-space:nowrap}.legend-node-icon{width:2rem;height:2rem;min-width:2rem;object-fit:contain}.legend-icon.misconfigured{width:1.2rem;height:1.2rem;min-width:1.2rem;background-image:url(/assets/images/visualizer/link-cut.png);background-size:contain;background-repeat:no-repeat}.legend-text2{display:inline-block;font-size:.625rem;font-weight:400;color:var(--tableText);padding-left:.125rem;padding-bottom:.25rem}@-webkit-keyframes rotating{0%{-webkit-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(360deg);-o-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes rotating{0%{-ms-transform:rotate(0deg);-moz-transform:rotate(0deg);-webkit-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0)}to{-ms-transform:rotate(360deg);-moz-transform:rotate(360deg);-webkit-transform:rotate(360deg);-o-transform:rotate(360deg);transform:rotate(360deg)}}.rotating{-webkit-animation:rotating 2s linear infinite;-moz-animation:rotating 2s linear infinite;-ms-animation:rotating 2s linear infinite;-o-animation:rotating 2s linear infinite;animation:rotating 2s linear infinite}::ng-deep .tool-tip-container{padding:0}::ng-deep .tool-tip-container .tooltip-header{font-family:Russo One,sans-serif;font-size:.975rem;color:var(--primary);padding:.75rem .9rem .45rem;border-bottom:1px solid var(--shadow)}::ng-deep .tool-tip-container .prop-row{display:flex;justify-content:space-between;gap:1.2rem;padding:.375rem .9rem;font-size:.9rem}::ng-deep .tool-tip-container .prop-row:nth-child(2n){background-color:var(--formBackground)}::ng-deep .tool-tip-container .prop-row:last-child{padding-bottom:.6rem}::ng-deep .tool-tip-container .prop-label{color:var(--tableText);opacity:.6;font-weight:500;white-space:nowrap}::ng-deep .tool-tip-container .prop-value{color:var(--tableText);font-weight:600;text-align:right}::ng-deep .cdk-overlay-pane:not(.mat-mdc-autocomplete-panel-above) div.mat-mdc-autocomplete-panel{border-radius:14px;background:var(--background);color:var(--tableText)}::ng-deep .mat-mdc-option.mdc-list-item{align-items:center;color:var(--tableText)}::ng-deep .mat-mdc-option.mdc-list-item:hover:not(.mat-option-disabled),::ng-deep .mat-mdc-option.mdc-list-item:focus:not(.mat-option-disabled){border-color:var(--primaryColor);border-radius:14px;border:.01rem solid var(--primaryColor)}::ng-deep .mat-mdc-raised-button .mdc-button__ripple{border:.1rem solid var(--primaryColor)}::ng-deep .p-button.p-button-icon-only{width:1.357rem}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i5$3.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i9.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "directive", type: i9.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "component", type: LoadingIndicatorComponent, selector: "lib-loading-indicator", inputs: ["isLoading"] }] }); }
|
|
12756
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: IdentityServicePathComponent, isStandalone: false, selector: "app-identity-visualizer", ngImport: i0, template: "<div [ngClass]=\"{ 'full-screen': fullScreen }\" class=\"modal solid\" id=\"IdentityVisualizer\">\n <div class=\"tp-data-header\">\n <div class=\"modaltitle\">Identity Service Path</div>\n <div (click)=\"hide()\" class=\"buttonBall icon-close\" id=\"EscZEndpointClose\"></div>\n <div (click)=\"toggleModalSize()\" class=\"buttonBall resize\" id=\"ResizeModalButton\"></div>\n </div>\n <div [ngClass]=\"isDarkMode==true?'darkMode':'lightMode'\" class=\"identity-topology-container\">\n <div class=\"topology-toolbar\">\n <div class=\"ep-topology-filter-container listBox row\">\n <input\n (keyup)=\"filterSearchArray()\"\n [(ngModel)]=\"selectedService\"\n [matAutocomplete]=\"auto\"\n class=\"topology-filter-input\"\n id=\"filterText\"\n matInput\n placeholder=\"Type to search identity accessible services.\"\n />\n\n @if (filterText) {\n <div\n (click)=\"clearSearchFilter()\"\n class=\"clear-svc-icon icon-close\"\n id=\"ClearSearchFilter\"\n ></div>\n }\n <div [hidden]=\"!noSearchResults\" class=\"no-results-warning\">No Results Found</div>\n <mat-autocomplete #auto=\"matAutocomplete\" (optionSelected)=\"autocompleteSearch($event)\">\n @for (option of autocompleteOptions; track option) {\n <mat-option [value]=\"option.name\">\n {{ option.name }}\n </mat-option>\n }\n </mat-autocomplete>\n </div>\n <div class=\"topology-refresh-controls\">\n <div\n (click)=\"refreshFabricData()\"\n [ngClass]=\"{ rotating: refreshRotate }\"\n class=\"refresh-button\"\n id=\"RefreshFabricButton\"\n title=\"Refresh fabric data\"\n >\n <div class=\"refresh-icon\"></div>\n </div>\n </div>\n </div>\n\n <div class=\"topotip\" id=\"epTooltip\"></div>\n\n <svg style='pointer-events:all' height=\"1000\" id=\"IdentityTopology\" width=\"1400\">\n </svg>\n <div class=\"topology-zoom-controls\">\n <div (click)=\"zoomIn()\" class=\"zoom-btn\" title=\"Zoom in\" id=\"ZoomInButton\">+</div>\n <div (click)=\"zoomOut()\" class=\"zoom-btn\" title=\"Zoom out\" id=\"ZoomOutButton\">−</div>\n <div (click)=\"resetView()\" class=\"zoom-btn zoom-btn-text\" title=\"Fit to content\" id=\"FitToContentButton\">Fit</div>\n </div>\n @if (isLoading) {\n <lib-loading-indicator [isLoading]=\"isLoading\"></lib-loading-indicator>\n }\n\n <div class=\"topology-legend\" [class.collapsed]=\"legendCollapsed\">\n <div class=\"legend-header\" (click)=\"legendCollapsed = !legendCollapsed\"\n [title]=\"legendCollapsed ? 'Show legend' : 'Hide legend'\">\n <span class=\"legend-header-title\">Legend</span>\n <span class=\"legend-header-toggle\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 16 16\" width=\"13\" height=\"13\">\n <path d=\"M3.5 6 L8 10.5 L12.5 6\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </span>\n </div>\n @if (!legendCollapsed) {\n <div class=\"legend-body\">\n <div class=\"legend-section\">\n <div class=\"legend-section-title\">Nodes</div>\n <div class=\"legend-item\" title=\"An endpoint identity \u2014 a client that dials the service or a tunneler that hosts it.\">\n <div class=\"legend-icon-col\"><img class=\"legend-node-icon\" src=\"assets/images/visualizer/host.png\" /></div>\n <div class=\"legend-text\">Identity</div>\n </div>\n <div class=\"legend-item\" title=\"A Ziti edge router that relays traffic between identities.\">\n <div class=\"legend-icon-col\"><img class=\"legend-node-icon\" src=\"assets/images/visualizer/Routers.png\" /></div>\n <div class=\"legend-text\">Edge Router</div>\n </div>\n <div class=\"legend-item\" title=\"The service being dialed and hosted.\">\n <div class=\"legend-icon-col\"><img class=\"legend-node-icon\" src=\"assets/images/visualizer/service.png\" /></div>\n <div class=\"legend-text\">Service</div>\n </div>\n </div>\n <div class=\"legend-section\">\n <div class=\"legend-section-title\">Status</div>\n <div class=\"legend-item\" title=\"Connected to an edge router.\">\n <div class=\"legend-icon-col\"><div class=\"legend-circle online\"></div></div>\n <div class=\"legend-text\">Online</div>\n </div>\n <div class=\"legend-item\" title=\"Enrolled but not currently connected (e.g. powered off or idle).\">\n <div class=\"legend-icon-col\"><div class=\"legend-circle offline\"></div></div>\n <div class=\"legend-text\">Offline</div>\n </div>\n <div class=\"legend-item\" title=\"Identity-only: the controller can't currently determine the connection state.\">\n <div class=\"legend-icon-col\"><div class=\"legend-circle unknown\"></div></div>\n <div class=\"legend-text\">Unknown</div>\n </div>\n <div class=\"legend-item\" title=\"Identity has not completed enrollment.\">\n <div class=\"legend-icon-col\"><div class=\"legend-circle notenrolled\"></div></div>\n <div class=\"legend-text\">Not enrolled</div>\n </div>\n </div>\n @if (fabricApiAvailable) {\n <div class=\"legend-section\">\n <div class=\"legend-section-title\">Path</div>\n <div class=\"legend-item\" title=\"Live traffic is flowing over this hop right now.\">\n <div class=\"legend-icon-col\"><div class=\"legend-line active-circuit\"></div></div>\n <div class=\"legend-text\">Active Circuit</div>\n </div>\n <div class=\"legend-item\" title=\"Reachable and authorized, but no active circuit at the moment.\">\n <div class=\"legend-icon-col\"><div class=\"legend-line available\"></div></div>\n <div class=\"legend-text\">Available</div>\n </div>\n <div class=\"legend-item\" title=\"This hop can't carry traffic right now. The node colors show which end is the problem \u2014 an offline router, or an unreachable / un-enrolled endpoint.\">\n <div class=\"legend-icon-col\"><div class=\"legend-line broken\"></div></div>\n <div class=\"legend-text\">Unavailable</div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n\n </div>\n</div>\n", styles: ["#IdentityVisualizer{overflow:hidden;width:75%;height:85%;background-color:var(--background);border-radius:1.5625rem;z-index:1001;margin:auto;inset:0rem;position:fixed;display:flex;flex-direction:column}#IdentityVisualizer .buttonBall{cursor:pointer;font-weight:600}#IdentityVisualizer .buttonBall.icon-close{position:absolute;top:1.25rem;right:1.25rem;font-size:1.4375rem;color:var(--icon)}#IdentityVisualizer .buttonBall:hover{filter:brightness(50%)}#IdentityVisualizer .clear-svc-icon{position:absolute;top:50%;right:.55rem;transform:translateY(-50%);width:1.65rem;height:1.65rem;display:flex;align-items:center;justify-content:center;border-radius:50%;font-size:.95rem;color:var(--icon);cursor:pointer;transition:background .12s ease,color .12s ease}#IdentityVisualizer .clear-svc-icon:hover{background:color-mix(in srgb,var(--tableText) 12%,transparent);color:var(--tableText)}#IdentityVisualizer.full-screen{width:100%;height:100%;border-radius:0}.ep-topology-filter-container{width:37%;display:flex;flex-direction:row;position:absolute;margin:1.25rem;padding:0}.topology-toolbar .ep-topology-filter-container{position:relative;margin:0;flex:1 1 auto;min-width:0;max-width:28rem}.topology-toolbar{display:flex;align-items:center;gap:1rem;padding:1.25rem 1.25rem .625rem;box-sizing:border-box;width:100%;flex-shrink:0;position:relative;z-index:4}.buttonBall.resize{top:1.25rem;width:1.4375rem;height:1.4375rem;right:3.4375rem;background-image:url(/assets/svgs/Resize.svg);background-size:1.4375rem 1.4375rem;position:absolute;cursor:pointer}.topology-refresh-controls{display:flex;align-items:center;gap:.5rem;flex:0 0 auto}.topology-refresh-controls .refresh-button{width:2.5rem;height:2.5rem;border-radius:50%;background-color:var(--navigation);box-shadow:0 .375rem 1rem 0 var(--shadow),0 0 0 .0625rem var(--shadow);display:flex;align-items:center;justify-content:center;cursor:pointer;transition:filter .15s ease,transform .1s ease,box-shadow .15s ease}.topology-refresh-controls .refresh-button .refresh-icon{width:1.125rem;height:1.125rem;background-color:var(--tableText);opacity:.85;-webkit-mask:url(/assets/svgs/refresh.svg) center/contain no-repeat;mask:url(/assets/svgs/refresh.svg) center/contain no-repeat;transition:opacity .15s ease}.topology-refresh-controls .refresh-button:hover{box-shadow:0 .5rem 1.25rem 0 var(--shadow),0 0 0 .0625rem var(--primaryColor)}.topology-refresh-controls .refresh-button:hover .refresh-icon{opacity:1}.topology-refresh-controls .refresh-button:active{transform:scale(.95)}.topology-refresh-controls .refresh-button.rotating .refresh-icon{animation:rotating 1s linear infinite}.topology-zoom-controls{position:absolute;bottom:1rem;left:1rem;display:flex;flex-direction:column;gap:.375rem;z-index:5}.topology-zoom-controls .zoom-btn{width:2.25rem;height:2.25rem;display:flex;align-items:center;justify-content:center;border-radius:.5rem;background-color:var(--navigation);color:var(--tableText);font-family:Open Sans,Arial,sans-serif;font-size:1.25rem;font-weight:600;line-height:1;cursor:pointer;-webkit-user-select:none;user-select:none;box-shadow:0 .5rem 1.25rem 0 var(--shadow);transition:filter .15s ease,transform .15s ease}.topology-zoom-controls .zoom-btn.zoom-btn-text{font-size:.75rem;text-transform:uppercase;letter-spacing:.04em}.topology-zoom-controls .zoom-btn:hover{filter:brightness(85%)}.topology-zoom-controls .zoom-btn:active{transform:scale(.95)}.identity-topology-container{display:flex;margin-left:-.3125rem;margin-top:.625rem;flex-direction:column;flex:1;overflow:hidden;position:relative}#IdentityTopology{cursor:grab;flex:1;min-height:0}#IdentityTopology:active{cursor:grabbing}.lightMode{background:#f5e9f2;background-image:linear-gradient(#ecd3e6 .125rem,transparent 0),linear-gradient(90deg,#ecd3e6 .125rem,transparent 0),linear-gradient(#ecd3e6 .0625rem,transparent 0),linear-gradient(90deg,#ecd3e6 .0625rem,transparent 0);background-size:2.5rem 2.5rem,2.5rem 2.5rem,.625rem .625rem,.625rem .625rem}.darkMode{background:#000a17;background-image:linear-gradient(#001127 .125rem,transparent 0),linear-gradient(90deg,#001127 .125rem,transparent 0),linear-gradient(#001127 1px,transparent 0),linear-gradient(90deg,#001127 1px,transparent 0);background-size:2.5rem 2.5rem,2.5rem 2.5rem,.625rem .625rem,.625rem .625rem}.tp-data-header{height:2.5rem;font-size:1.5625rem;font-weight:600;margin-left:1.25rem;margin-top:.625rem;margin-bottom:.3125rem;font-family:Russo One;color:var(--primaryColor);font-size:1.875rem;text-transform:uppercase;font-weight:400}.node text{font:.75rem Open Sans,Arial,sans-serif}.modaltitle{padding-top:.375rem;padding-left:.3125rem;color:var(--primaryColor)}.modalbuttons{padding-right:1.25rem}.listBox.row .close{position:fixed;display:flex;flex-direction:column;font-family:icomoon!important;font-style:normal;font-weight:600;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--icon);text-align:center;font-size:2.1875rem;line-height:2.5rem;left:35%;top:17%;width:2.5rem;height:2.5rem;background-image:url(/assets/svgs/close.svg);z-index:600;background-position:center center;background-size:75%;cursor:pointer;transition:.3s;justify-content:end;align-items:center}.tp-topo-data-header{border:0rem solid black;height:75rem;font-size:.75rem;font-weight:400;margin-left:.75rem;margin-top:.1875rem;color:var(--primaryColor)}.services-select{width:25rem;flex-shrink:0;border-radius:1.6875rem;height:3.1875rem;margin-left:1.375rem;margin-top:.625rem;background-color:#fff;border-color:var(--navigation);box-shadow:0 1.25rem 1.4375rem 0 var(--shadow);color:var(--tableText);padding-left:1.25rem}select{background-image:url(/assets/svgs/ArrowDown.svg);background-position:center right .9375rem;background-repeat:no-repeat;background-size:1.125rem;padding-right:2.9375rem}.topology-filter-input{height:2.75rem;width:100%;box-sizing:border-box;border-radius:.75rem;border:1px solid var(--shadow);background-color:var(--background);color:var(--tableText);box-shadow:0 .0625rem .125rem #0000000f;padding:0 2.6rem 0 1rem;font-size:.95rem;outline:none;transition:border-color .15s ease,box-shadow .15s ease}.topology-filter-input::placeholder{color:color-mix(in srgb,var(--tableText) 50%,transparent)}.topology-filter-input:hover{border-color:color-mix(in srgb,var(--tableText) 28%,transparent)}.topology-filter-input:focus{border-color:var(--primary);box-shadow:0 0 0 .1875rem color-mix(in srgb,var(--primary) 18%,transparent)}::ng-deep .topotip{display:none;position:fixed;background:var(--navigation);border-radius:.5rem;pointer-events:auto;box-shadow:0 .25rem 1rem 0 var(--shadow);color:var(--tableText);z-index:2000;padding:0;max-width:24rem;overflow:hidden}.topology-legend{width:15rem;border-radius:.5rem;position:absolute;bottom:1rem;right:.625rem;background:var(--navigation);box-shadow:0 .25rem .75rem 0 var(--shadow);padding:.9rem}.topology-legend.collapsed{width:auto}.legend-header{display:flex;align-items:center;justify-content:space-between;gap:.75rem;cursor:pointer;-webkit-user-select:none;user-select:none}.legend-header-title{font-family:Russo One,sans-serif;font-size:.9rem;text-transform:uppercase;letter-spacing:.05em;color:var(--primary)}.legend-header-toggle{color:var(--primary);display:flex;align-items:center}.legend-header-toggle svg{display:block;transition:transform .15s ease}.topology-legend.collapsed .legend-header-toggle svg{transform:rotate(180deg)}.legend-body{padding-top:.6rem}.legend-section{padding:.3rem 0}.legend-section:first-child{padding-top:0}.legend-section:last-child{padding-bottom:0;margin-bottom:-.5rem}.legend-section:not(:last-child){border-bottom:1px solid var(--shadow)}.legend-section-title{font-family:Russo One,sans-serif;font-size:.9rem;text-transform:uppercase;letter-spacing:.05em;color:var(--primary);margin-bottom:.25rem}.legend-item{display:flex;align-items:center;gap:0;padding:.225rem 0;cursor:help}.legend-icon-col{width:2.5rem;min-width:2.5rem;display:flex;align-items:center;justify-content:center;margin-right:.4rem}.legend-circle{height:.75rem;width:.75rem;min-width:.75rem;border-radius:50%}.legend-circle.online{background-color:#08dc5a}.legend-circle.offline{background-color:#8a8f9a}.legend-circle.unknown{background-color:#e6a700}.legend-circle.notenrolled{background-color:var(--background);border:.125rem solid #8a8f9a}.legend-line{height:.225rem;width:1.5rem}.legend-line.active-circuit{background-color:#00cd13}.legend-line.available{background:repeating-linear-gradient(to right,#8a8f9a 0,#8a8f9a .25rem,transparent .25rem,transparent .5rem)}.legend-line.broken{height:0;background:none;border-top:.28rem dotted #e6432e}.legend-text{font-size:.975rem;font-weight:500;color:var(--tableText);white-space:nowrap}.legend-node-icon{width:2rem;height:2rem;min-width:2rem;object-fit:contain}.legend-icon.misconfigured,.legend-icon.broken-link{width:1.2rem;height:1.2rem;min-width:1.2rem;background-image:url(/assets/images/visualizer/link-cut.png);background-size:contain;background-repeat:no-repeat}.legend-text2{display:inline-block;font-size:.625rem;font-weight:400;color:var(--tableText);padding-left:.125rem;padding-bottom:.25rem}@-webkit-keyframes rotating{0%{-webkit-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(360deg);-o-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes rotating{0%{-ms-transform:rotate(0deg);-moz-transform:rotate(0deg);-webkit-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0)}to{-ms-transform:rotate(360deg);-moz-transform:rotate(360deg);-webkit-transform:rotate(360deg);-o-transform:rotate(360deg);transform:rotate(360deg)}}.rotating{-webkit-animation:rotating 2s linear infinite;-moz-animation:rotating 2s linear infinite;-ms-animation:rotating 2s linear infinite;-o-animation:rotating 2s linear infinite;animation:rotating 2s linear infinite}::ng-deep .tool-tip-container{padding:0}::ng-deep .tool-tip-container .tooltip-header{font-family:Russo One,sans-serif;font-size:.975rem;color:var(--primary);padding:.7rem .9rem .15rem}::ng-deep .tool-tip-container .prop-row{display:flex;justify-content:space-between;gap:1.2rem;padding:.375rem .9rem;font-size:.9rem}::ng-deep .tool-tip-container .prop-row:nth-child(2n){background-color:var(--formBackground)}::ng-deep .tool-tip-container .prop-row:last-child{padding-bottom:.6rem}::ng-deep .tool-tip-container .prop-label{color:var(--tableText);opacity:.6;font-weight:500;white-space:nowrap}::ng-deep .tool-tip-container .prop-value{color:var(--tableText);font-weight:600;text-align:right}::ng-deep .tool-tip-container .tt-status-pill{display:inline-flex;align-items:center;gap:.35rem;margin:0 .9rem .55rem;padding:.14rem .55rem .14rem .45rem;border-radius:999px;font-size:.72rem;font-weight:600;letter-spacing:.02em}::ng-deep .tool-tip-container .tt-status-dot{width:.45rem;height:.45rem;min-width:.45rem;border-radius:50%}::ng-deep .tool-tip-container .prop-row:first-of-type{border-top:1px solid var(--shadow);padding-top:.5rem}::ng-deep .tool-tip-container .tt-ok{background:#08dc5a1f}::ng-deep .tool-tip-container .tt-ok .tt-status-dot{background:#08dc5a}::ng-deep .tool-tip-container .tt-ok .tt-status-label{color:#0a9d4a}::ng-deep .tool-tip-container .tt-broken{background:#e6432e1f}::ng-deep .tool-tip-container .tt-broken .tt-status-dot{background:#e6432e}::ng-deep .tool-tip-container .tt-broken .tt-status-label{color:#d23b27}::ng-deep .tool-tip-container .tt-offline{background:#8a8f9a29}::ng-deep .tool-tip-container .tt-offline .tt-status-dot{background:#8a8f9a}::ng-deep .tool-tip-container .tt-offline .tt-status-label{color:#6f7480}::ng-deep .tool-tip-container .tt-unknown{background:#e6a70024}::ng-deep .tool-tip-container .tt-unknown .tt-status-dot{background:#e6a700}::ng-deep .tool-tip-container .tt-unknown .tt-status-label{color:#b07f00}::ng-deep .tool-tip-container .tt-neutral{background:#6e78c81f}::ng-deep .tool-tip-container .tt-neutral .tt-status-dot{background:var(--primary)}::ng-deep .tool-tip-container .tt-neutral .tt-status-label{color:var(--primary)}::ng-deep .cdk-overlay-pane .mat-mdc-autocomplete-panel{border-radius:.75rem!important;background:var(--background);color:var(--tableText);padding:.35rem;border:1px solid var(--shadow);box-shadow:0 .5rem 1.5rem #00000029!important}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mdc-list-item{align-items:center;min-height:2.5rem;margin:.1rem 0;border-radius:.5rem;color:var(--tableText);transition:background-color .12s ease,color .12s ease}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mdc-list-item:hover:not(.mdc-list-item--disabled),::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mat-mdc-option-active:not(.mdc-list-item--disabled){background-color:color-mix(in srgb,var(--primary) 9%,transparent)!important}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mdc-list-item--selected:not(.mdc-list-item--disabled){background-color:color-mix(in srgb,var(--primary) 14%,transparent)!important}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mdc-list-item--selected .mdc-list-item__primary-text{color:var(--primary);font-weight:600}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .mat-mdc-option-ripple,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .mat-ripple{display:none}::ng-deep .mat-mdc-autocomplete-panel .mat-pseudo-checkbox-minimal{color:var(--primary)}::ng-deep .mat-mdc-raised-button .mdc-button__ripple{border:.1rem solid var(--primaryColor)}::ng-deep .p-button.p-button-icon-only{width:1.357rem}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i5$3.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i9.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "directive", type: i9.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "component", type: LoadingIndicatorComponent, selector: "lib-loading-indicator", inputs: ["isLoading"] }] }); }
|
|
12365
12757
|
}
|
|
12366
12758
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: IdentityServicePathComponent, decorators: [{
|
|
12367
12759
|
type: Component,
|
|
12368
|
-
args: [{ selector: 'app-identity-visualizer', standalone: false, template: "<div [ngClass]=\"{ 'full-screen': fullScreen }\" class=\"modal solid\" id=\"IdentityVisualizer\">\n <div class=\"tp-data-header\">\n <div class=\"modaltitle\">Identity Service Path</div>\n <div (click)=\"hide()\" class=\"buttonBall icon-close\" id=\"EscZEndpointClose\"></div>\n <div (click)=\"toggleModalSize()\" class=\"buttonBall resize\" id=\"ResizeModalButton\"></div>\n </div>\n <div [ngClass]=\"isDarkMode==true?'darkMode':'lightMode'\" class=\"identity-topology-container\">\n <div style=\"width: 100%; height: 80px; float: left\">\n <div class=\"ep-topology-filter-container listBox row\">\n <input\n (keyup)=\"filterSearchArray()\"\n [(ngModel)]=\"selectedService\"\n [matAutocomplete]=\"auto\"\n class=\"topology-filter-input\"\n id=\"filterText\"\n matInput\n placeholder=\"Type to search identity accessible services.\"\n />\n\n @if (filterText) {\n <div\n (click)=\"clearSearchFilter()\"\n class=\"clear-svc-icon icon-close\"\n id=\"ClearSearchFilter\"\n ></div>\n }\n <div [hidden]=\"!noSearchResults\" class=\"no-results-warning\">No Results Found</div>\n <mat-autocomplete #auto=\"matAutocomplete\" (optionSelected)=\"autocompleteSearch($event)\">\n @for (option of autocompleteOptions; track option) {\n <mat-option [value]=\"option.name\">\n {{ option.name }}\n </mat-option>\n }\n </mat-autocomplete>\n </div>\n </div>\n\n <div class=\"topotip\" id=\"epTooltip\"></div>\n\n <svg style='pointer-events:all' height=\"1000\" id=\"IdentityTopology\" width=\"1400\">\n </svg>\n @if (isLoading) {\n <lib-loading-indicator [isLoading]=\"isLoading\"></lib-loading-indicator>\n }\n\n <div class=\"topology-legend\">\n <div class=\"legend-section\">\n <div class=\"legend-section-title\">Nodes</div>\n <div class=\"legend-item\">\n <div class=\"legend-icon-col\"><img class=\"legend-node-icon\" src=\"assets/images/visualizer/host.png\" /></div>\n <div class=\"legend-text\">Identity</div>\n </div>\n <div class=\"legend-item\">\n <div class=\"legend-icon-col\"><img class=\"legend-node-icon\" src=\"assets/images/visualizer/Routers.png\" /></div>\n <div class=\"legend-text\">Edge Router</div>\n </div>\n <div class=\"legend-item\">\n <div class=\"legend-icon-col\"><img class=\"legend-node-icon\" src=\"assets/images/visualizer/service.png\" /></div>\n <div class=\"legend-text\">Service</div>\n </div>\n </div>\n <div class=\"legend-section\">\n <div class=\"legend-section-title\">Status</div>\n <div class=\"legend-item\">\n <div class=\"legend-icon-col\"><div class=\"legend-circle registered_in_use\"></div></div>\n <div class=\"legend-text\">Online</div>\n </div>\n <div class=\"legend-item\">\n <div class=\"legend-icon-col\"><div class=\"legend-circle unregistered\"></div></div>\n <div class=\"legend-text\">Offline</div>\n </div>\n <div class=\"legend-item\">\n <div class=\"legend-icon-col\"><div class=\"legend-circle errored\"></div></div>\n <div class=\"legend-text\">Error</div>\n </div>\n </div>\n @if (fabricApiAvailable) {\n <div class=\"legend-section\">\n <div class=\"legend-section-title\">Path</div>\n <div class=\"legend-item\">\n <div class=\"legend-icon-col\"><div class=\"legend-line active-circuit\"></div></div>\n <div class=\"legend-text\">Active Circuit</div>\n </div>\n <div class=\"legend-item\">\n <div class=\"legend-icon-col\"><div class=\"legend-line available\"></div></div>\n <div class=\"legend-text\">Available</div>\n </div>\n <div class=\"legend-item\">\n <div class=\"legend-icon-col\"><div class=\"legend-icon misconfigured\"></div></div>\n <div class=\"legend-text\">Broken</div>\n </div>\n </div>\n }\n </div>\n\n </div>\n</div>\n", styles: ["#IdentityVisualizer{overflow:hidden;width:75%;height:85%;background-color:var(--background);border-radius:1.5625rem;z-index:1001;margin:auto;inset:0rem;position:fixed;display:flex;flex-direction:column}#IdentityVisualizer .buttonBall{cursor:pointer;font-weight:600}#IdentityVisualizer .buttonBall.icon-close{position:absolute;top:1.25rem;right:1.25rem;font-size:1.4375rem;color:var(--icon)}#IdentityVisualizer .buttonBall:hover{filter:brightness(50%)}#IdentityVisualizer .clear-svc-icon{font-size:1.4375rem;font-weight:600;position:relative;top:.9375rem;right:2.5rem;color:var(--icon);cursor:pointer}#IdentityVisualizer .clear-svc-icon:hover{filter:brightness(50%)}#IdentityVisualizer.full-screen{width:100%;height:100%;border-radius:0}.ep-topology-filter-container{width:37%;display:flex;flex-direction:row;position:absolute;margin:1.25rem;padding:0}.buttonBall.resize{top:1.25rem;width:1.4375rem;height:1.4375rem;right:3.4375rem;background-image:url(/assets/svgs/Resize.svg);background-size:1.4375rem 1.4375rem;position:absolute;cursor:pointer}.identity-topology-container{display:flex;margin-left:-.3125rem;margin-top:.625rem;flex-direction:column;cursor:grab;flex:1;overflow:hidden}.lightMode{background:#f5e9f2;background-image:linear-gradient(#ecd3e6 .125rem,transparent 0),linear-gradient(90deg,#ecd3e6 .125rem,transparent 0),linear-gradient(#ecd3e6 .0625rem,transparent 0),linear-gradient(90deg,#ecd3e6 .0625rem,transparent 0);background-size:2.5rem 2.5rem,2.5rem 2.5rem,.625rem .625rem,.625rem .625rem}.darkMode{background:#000a17;background-image:linear-gradient(#001127 .125rem,transparent 0),linear-gradient(90deg,#001127 .125rem,transparent 0),linear-gradient(#001127 1px,transparent 0),linear-gradient(90deg,#001127 1px,transparent 0);background-size:2.5rem 2.5rem,2.5rem 2.5rem,.625rem .625rem,.625rem .625rem}.tp-data-header{height:2.5rem;font-size:1.5625rem;font-weight:600;margin-left:1.25rem;margin-top:.625rem;margin-bottom:.3125rem;font-family:Russo One;color:var(--primaryColor);font-size:1.875rem;text-transform:uppercase;font-weight:400}.node text{font:.75rem Open Sans,Arial,sans-serif}.modaltitle{padding-top:.375rem;padding-left:.3125rem;color:var(--primaryColor)}.modalbuttons{padding-right:1.25rem}.listBox.row .close{position:fixed;display:flex;flex-direction:column;font-family:icomoon!important;font-style:normal;font-weight:600;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--icon);text-align:center;font-size:2.1875rem;line-height:2.5rem;left:35%;top:17%;width:2.5rem;height:2.5rem;background-image:url(/assets/svgs/close.svg);z-index:600;background-position:center center;background-size:75%;cursor:pointer;transition:.3s;justify-content:end;align-items:center}.tp-topo-data-header{border:0rem solid black;height:75rem;font-size:.75rem;font-weight:400;margin-left:.75rem;margin-top:.1875rem;color:var(--primaryColor)}.services-select{width:25rem;flex-shrink:0;border-radius:1.6875rem;height:3.1875rem;margin-left:1.375rem;margin-top:.625rem;background-color:#fff;border-color:var(--navigation);box-shadow:0 1.25rem 1.4375rem 0 var(--shadow);color:var(--tableText);padding-left:1.25rem}select{background-image:url(/assets/svgs/ArrowDown.svg);background-position:center right .9375rem;background-repeat:no-repeat;background-size:1.125rem;padding-right:2.9375rem}.topology-filter-input{height:3.125rem;width:95%;border-radius:1.5625rem;border-color:var(--navigation);background-color:var(--navigation);box-shadow:0 1.25rem 1.4375rem 0 var(--shadow);padding-left:1.25rem}::ng-deep .topotip{display:none;position:fixed;background:var(--navigation);border-radius:.5rem;pointer-events:none;box-shadow:0 .25rem 1rem 0 var(--shadow);color:var(--tableText);z-index:2000;padding:0;max-width:24rem;overflow:hidden}.topology-legend{width:13rem;border-radius:.5rem;position:absolute;bottom:1rem;right:.625rem;background:var(--navigation);box-shadow:0 .25rem .75rem 0 var(--shadow);padding:.9rem}.legend-section{padding:.3rem 0}.legend-section:first-child{padding-top:0}.legend-section:last-child{padding-bottom:0;margin-bottom:-.5rem}.legend-section:not(:last-child){border-bottom:1px solid var(--shadow)}.legend-section-title{font-family:Russo One,sans-serif;font-size:.9rem;text-transform:uppercase;letter-spacing:.05em;color:var(--primary);margin-bottom:.25rem}.legend-item{display:flex;align-items:center;gap:0;padding:.225rem 0}.legend-icon-col{width:2.5rem;min-width:2.5rem;display:flex;align-items:center;justify-content:center;margin-right:.4rem}.legend-circle{height:.75rem;width:.75rem;min-width:.75rem;border-radius:50%}.legend-circle.unregistered{background-color:#8a8f9a}.legend-circle.registered_in_use{background-color:#08dc5a}.legend-circle.errored{background-color:#fb0d3d}.legend-line{height:.225rem;width:1.5rem}.legend-line.active-circuit{background-color:#00cd13}.legend-line.available{background:repeating-linear-gradient(to right,#8a8f9a 0,#8a8f9a .25rem,transparent .25rem,transparent .5rem)}.legend-text{font-size:.975rem;font-weight:500;color:var(--tableText);white-space:nowrap}.legend-node-icon{width:2rem;height:2rem;min-width:2rem;object-fit:contain}.legend-icon.misconfigured{width:1.2rem;height:1.2rem;min-width:1.2rem;background-image:url(/assets/images/visualizer/link-cut.png);background-size:contain;background-repeat:no-repeat}.legend-text2{display:inline-block;font-size:.625rem;font-weight:400;color:var(--tableText);padding-left:.125rem;padding-bottom:.25rem}@-webkit-keyframes rotating{0%{-webkit-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(360deg);-o-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes rotating{0%{-ms-transform:rotate(0deg);-moz-transform:rotate(0deg);-webkit-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0)}to{-ms-transform:rotate(360deg);-moz-transform:rotate(360deg);-webkit-transform:rotate(360deg);-o-transform:rotate(360deg);transform:rotate(360deg)}}.rotating{-webkit-animation:rotating 2s linear infinite;-moz-animation:rotating 2s linear infinite;-ms-animation:rotating 2s linear infinite;-o-animation:rotating 2s linear infinite;animation:rotating 2s linear infinite}::ng-deep .tool-tip-container{padding:0}::ng-deep .tool-tip-container .tooltip-header{font-family:Russo One,sans-serif;font-size:.975rem;color:var(--primary);padding:.75rem .9rem .45rem;border-bottom:1px solid var(--shadow)}::ng-deep .tool-tip-container .prop-row{display:flex;justify-content:space-between;gap:1.2rem;padding:.375rem .9rem;font-size:.9rem}::ng-deep .tool-tip-container .prop-row:nth-child(2n){background-color:var(--formBackground)}::ng-deep .tool-tip-container .prop-row:last-child{padding-bottom:.6rem}::ng-deep .tool-tip-container .prop-label{color:var(--tableText);opacity:.6;font-weight:500;white-space:nowrap}::ng-deep .tool-tip-container .prop-value{color:var(--tableText);font-weight:600;text-align:right}::ng-deep .cdk-overlay-pane:not(.mat-mdc-autocomplete-panel-above) div.mat-mdc-autocomplete-panel{border-radius:14px;background:var(--background);color:var(--tableText)}::ng-deep .mat-mdc-option.mdc-list-item{align-items:center;color:var(--tableText)}::ng-deep .mat-mdc-option.mdc-list-item:hover:not(.mat-option-disabled),::ng-deep .mat-mdc-option.mdc-list-item:focus:not(.mat-option-disabled){border-color:var(--primaryColor);border-radius:14px;border:.01rem solid var(--primaryColor)}::ng-deep .mat-mdc-raised-button .mdc-button__ripple{border:.1rem solid var(--primaryColor)}::ng-deep .p-button.p-button-icon-only{width:1.357rem}\n"] }]
|
|
12760
|
+
args: [{ selector: 'app-identity-visualizer', standalone: false, template: "<div [ngClass]=\"{ 'full-screen': fullScreen }\" class=\"modal solid\" id=\"IdentityVisualizer\">\n <div class=\"tp-data-header\">\n <div class=\"modaltitle\">Identity Service Path</div>\n <div (click)=\"hide()\" class=\"buttonBall icon-close\" id=\"EscZEndpointClose\"></div>\n <div (click)=\"toggleModalSize()\" class=\"buttonBall resize\" id=\"ResizeModalButton\"></div>\n </div>\n <div [ngClass]=\"isDarkMode==true?'darkMode':'lightMode'\" class=\"identity-topology-container\">\n <div class=\"topology-toolbar\">\n <div class=\"ep-topology-filter-container listBox row\">\n <input\n (keyup)=\"filterSearchArray()\"\n [(ngModel)]=\"selectedService\"\n [matAutocomplete]=\"auto\"\n class=\"topology-filter-input\"\n id=\"filterText\"\n matInput\n placeholder=\"Type to search identity accessible services.\"\n />\n\n @if (filterText) {\n <div\n (click)=\"clearSearchFilter()\"\n class=\"clear-svc-icon icon-close\"\n id=\"ClearSearchFilter\"\n ></div>\n }\n <div [hidden]=\"!noSearchResults\" class=\"no-results-warning\">No Results Found</div>\n <mat-autocomplete #auto=\"matAutocomplete\" (optionSelected)=\"autocompleteSearch($event)\">\n @for (option of autocompleteOptions; track option) {\n <mat-option [value]=\"option.name\">\n {{ option.name }}\n </mat-option>\n }\n </mat-autocomplete>\n </div>\n <div class=\"topology-refresh-controls\">\n <div\n (click)=\"refreshFabricData()\"\n [ngClass]=\"{ rotating: refreshRotate }\"\n class=\"refresh-button\"\n id=\"RefreshFabricButton\"\n title=\"Refresh fabric data\"\n >\n <div class=\"refresh-icon\"></div>\n </div>\n </div>\n </div>\n\n <div class=\"topotip\" id=\"epTooltip\"></div>\n\n <svg style='pointer-events:all' height=\"1000\" id=\"IdentityTopology\" width=\"1400\">\n </svg>\n <div class=\"topology-zoom-controls\">\n <div (click)=\"zoomIn()\" class=\"zoom-btn\" title=\"Zoom in\" id=\"ZoomInButton\">+</div>\n <div (click)=\"zoomOut()\" class=\"zoom-btn\" title=\"Zoom out\" id=\"ZoomOutButton\">−</div>\n <div (click)=\"resetView()\" class=\"zoom-btn zoom-btn-text\" title=\"Fit to content\" id=\"FitToContentButton\">Fit</div>\n </div>\n @if (isLoading) {\n <lib-loading-indicator [isLoading]=\"isLoading\"></lib-loading-indicator>\n }\n\n <div class=\"topology-legend\" [class.collapsed]=\"legendCollapsed\">\n <div class=\"legend-header\" (click)=\"legendCollapsed = !legendCollapsed\"\n [title]=\"legendCollapsed ? 'Show legend' : 'Hide legend'\">\n <span class=\"legend-header-title\">Legend</span>\n <span class=\"legend-header-toggle\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 16 16\" width=\"13\" height=\"13\">\n <path d=\"M3.5 6 L8 10.5 L12.5 6\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </span>\n </div>\n @if (!legendCollapsed) {\n <div class=\"legend-body\">\n <div class=\"legend-section\">\n <div class=\"legend-section-title\">Nodes</div>\n <div class=\"legend-item\" title=\"An endpoint identity \u2014 a client that dials the service or a tunneler that hosts it.\">\n <div class=\"legend-icon-col\"><img class=\"legend-node-icon\" src=\"assets/images/visualizer/host.png\" /></div>\n <div class=\"legend-text\">Identity</div>\n </div>\n <div class=\"legend-item\" title=\"A Ziti edge router that relays traffic between identities.\">\n <div class=\"legend-icon-col\"><img class=\"legend-node-icon\" src=\"assets/images/visualizer/Routers.png\" /></div>\n <div class=\"legend-text\">Edge Router</div>\n </div>\n <div class=\"legend-item\" title=\"The service being dialed and hosted.\">\n <div class=\"legend-icon-col\"><img class=\"legend-node-icon\" src=\"assets/images/visualizer/service.png\" /></div>\n <div class=\"legend-text\">Service</div>\n </div>\n </div>\n <div class=\"legend-section\">\n <div class=\"legend-section-title\">Status</div>\n <div class=\"legend-item\" title=\"Connected to an edge router.\">\n <div class=\"legend-icon-col\"><div class=\"legend-circle online\"></div></div>\n <div class=\"legend-text\">Online</div>\n </div>\n <div class=\"legend-item\" title=\"Enrolled but not currently connected (e.g. powered off or idle).\">\n <div class=\"legend-icon-col\"><div class=\"legend-circle offline\"></div></div>\n <div class=\"legend-text\">Offline</div>\n </div>\n <div class=\"legend-item\" title=\"Identity-only: the controller can't currently determine the connection state.\">\n <div class=\"legend-icon-col\"><div class=\"legend-circle unknown\"></div></div>\n <div class=\"legend-text\">Unknown</div>\n </div>\n <div class=\"legend-item\" title=\"Identity has not completed enrollment.\">\n <div class=\"legend-icon-col\"><div class=\"legend-circle notenrolled\"></div></div>\n <div class=\"legend-text\">Not enrolled</div>\n </div>\n </div>\n @if (fabricApiAvailable) {\n <div class=\"legend-section\">\n <div class=\"legend-section-title\">Path</div>\n <div class=\"legend-item\" title=\"Live traffic is flowing over this hop right now.\">\n <div class=\"legend-icon-col\"><div class=\"legend-line active-circuit\"></div></div>\n <div class=\"legend-text\">Active Circuit</div>\n </div>\n <div class=\"legend-item\" title=\"Reachable and authorized, but no active circuit at the moment.\">\n <div class=\"legend-icon-col\"><div class=\"legend-line available\"></div></div>\n <div class=\"legend-text\">Available</div>\n </div>\n <div class=\"legend-item\" title=\"This hop can't carry traffic right now. The node colors show which end is the problem \u2014 an offline router, or an unreachable / un-enrolled endpoint.\">\n <div class=\"legend-icon-col\"><div class=\"legend-line broken\"></div></div>\n <div class=\"legend-text\">Unavailable</div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n\n </div>\n</div>\n", styles: ["#IdentityVisualizer{overflow:hidden;width:75%;height:85%;background-color:var(--background);border-radius:1.5625rem;z-index:1001;margin:auto;inset:0rem;position:fixed;display:flex;flex-direction:column}#IdentityVisualizer .buttonBall{cursor:pointer;font-weight:600}#IdentityVisualizer .buttonBall.icon-close{position:absolute;top:1.25rem;right:1.25rem;font-size:1.4375rem;color:var(--icon)}#IdentityVisualizer .buttonBall:hover{filter:brightness(50%)}#IdentityVisualizer .clear-svc-icon{position:absolute;top:50%;right:.55rem;transform:translateY(-50%);width:1.65rem;height:1.65rem;display:flex;align-items:center;justify-content:center;border-radius:50%;font-size:.95rem;color:var(--icon);cursor:pointer;transition:background .12s ease,color .12s ease}#IdentityVisualizer .clear-svc-icon:hover{background:color-mix(in srgb,var(--tableText) 12%,transparent);color:var(--tableText)}#IdentityVisualizer.full-screen{width:100%;height:100%;border-radius:0}.ep-topology-filter-container{width:37%;display:flex;flex-direction:row;position:absolute;margin:1.25rem;padding:0}.topology-toolbar .ep-topology-filter-container{position:relative;margin:0;flex:1 1 auto;min-width:0;max-width:28rem}.topology-toolbar{display:flex;align-items:center;gap:1rem;padding:1.25rem 1.25rem .625rem;box-sizing:border-box;width:100%;flex-shrink:0;position:relative;z-index:4}.buttonBall.resize{top:1.25rem;width:1.4375rem;height:1.4375rem;right:3.4375rem;background-image:url(/assets/svgs/Resize.svg);background-size:1.4375rem 1.4375rem;position:absolute;cursor:pointer}.topology-refresh-controls{display:flex;align-items:center;gap:.5rem;flex:0 0 auto}.topology-refresh-controls .refresh-button{width:2.5rem;height:2.5rem;border-radius:50%;background-color:var(--navigation);box-shadow:0 .375rem 1rem 0 var(--shadow),0 0 0 .0625rem var(--shadow);display:flex;align-items:center;justify-content:center;cursor:pointer;transition:filter .15s ease,transform .1s ease,box-shadow .15s ease}.topology-refresh-controls .refresh-button .refresh-icon{width:1.125rem;height:1.125rem;background-color:var(--tableText);opacity:.85;-webkit-mask:url(/assets/svgs/refresh.svg) center/contain no-repeat;mask:url(/assets/svgs/refresh.svg) center/contain no-repeat;transition:opacity .15s ease}.topology-refresh-controls .refresh-button:hover{box-shadow:0 .5rem 1.25rem 0 var(--shadow),0 0 0 .0625rem var(--primaryColor)}.topology-refresh-controls .refresh-button:hover .refresh-icon{opacity:1}.topology-refresh-controls .refresh-button:active{transform:scale(.95)}.topology-refresh-controls .refresh-button.rotating .refresh-icon{animation:rotating 1s linear infinite}.topology-zoom-controls{position:absolute;bottom:1rem;left:1rem;display:flex;flex-direction:column;gap:.375rem;z-index:5}.topology-zoom-controls .zoom-btn{width:2.25rem;height:2.25rem;display:flex;align-items:center;justify-content:center;border-radius:.5rem;background-color:var(--navigation);color:var(--tableText);font-family:Open Sans,Arial,sans-serif;font-size:1.25rem;font-weight:600;line-height:1;cursor:pointer;-webkit-user-select:none;user-select:none;box-shadow:0 .5rem 1.25rem 0 var(--shadow);transition:filter .15s ease,transform .15s ease}.topology-zoom-controls .zoom-btn.zoom-btn-text{font-size:.75rem;text-transform:uppercase;letter-spacing:.04em}.topology-zoom-controls .zoom-btn:hover{filter:brightness(85%)}.topology-zoom-controls .zoom-btn:active{transform:scale(.95)}.identity-topology-container{display:flex;margin-left:-.3125rem;margin-top:.625rem;flex-direction:column;flex:1;overflow:hidden;position:relative}#IdentityTopology{cursor:grab;flex:1;min-height:0}#IdentityTopology:active{cursor:grabbing}.lightMode{background:#f5e9f2;background-image:linear-gradient(#ecd3e6 .125rem,transparent 0),linear-gradient(90deg,#ecd3e6 .125rem,transparent 0),linear-gradient(#ecd3e6 .0625rem,transparent 0),linear-gradient(90deg,#ecd3e6 .0625rem,transparent 0);background-size:2.5rem 2.5rem,2.5rem 2.5rem,.625rem .625rem,.625rem .625rem}.darkMode{background:#000a17;background-image:linear-gradient(#001127 .125rem,transparent 0),linear-gradient(90deg,#001127 .125rem,transparent 0),linear-gradient(#001127 1px,transparent 0),linear-gradient(90deg,#001127 1px,transparent 0);background-size:2.5rem 2.5rem,2.5rem 2.5rem,.625rem .625rem,.625rem .625rem}.tp-data-header{height:2.5rem;font-size:1.5625rem;font-weight:600;margin-left:1.25rem;margin-top:.625rem;margin-bottom:.3125rem;font-family:Russo One;color:var(--primaryColor);font-size:1.875rem;text-transform:uppercase;font-weight:400}.node text{font:.75rem Open Sans,Arial,sans-serif}.modaltitle{padding-top:.375rem;padding-left:.3125rem;color:var(--primaryColor)}.modalbuttons{padding-right:1.25rem}.listBox.row .close{position:fixed;display:flex;flex-direction:column;font-family:icomoon!important;font-style:normal;font-weight:600;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:var(--icon);text-align:center;font-size:2.1875rem;line-height:2.5rem;left:35%;top:17%;width:2.5rem;height:2.5rem;background-image:url(/assets/svgs/close.svg);z-index:600;background-position:center center;background-size:75%;cursor:pointer;transition:.3s;justify-content:end;align-items:center}.tp-topo-data-header{border:0rem solid black;height:75rem;font-size:.75rem;font-weight:400;margin-left:.75rem;margin-top:.1875rem;color:var(--primaryColor)}.services-select{width:25rem;flex-shrink:0;border-radius:1.6875rem;height:3.1875rem;margin-left:1.375rem;margin-top:.625rem;background-color:#fff;border-color:var(--navigation);box-shadow:0 1.25rem 1.4375rem 0 var(--shadow);color:var(--tableText);padding-left:1.25rem}select{background-image:url(/assets/svgs/ArrowDown.svg);background-position:center right .9375rem;background-repeat:no-repeat;background-size:1.125rem;padding-right:2.9375rem}.topology-filter-input{height:2.75rem;width:100%;box-sizing:border-box;border-radius:.75rem;border:1px solid var(--shadow);background-color:var(--background);color:var(--tableText);box-shadow:0 .0625rem .125rem #0000000f;padding:0 2.6rem 0 1rem;font-size:.95rem;outline:none;transition:border-color .15s ease,box-shadow .15s ease}.topology-filter-input::placeholder{color:color-mix(in srgb,var(--tableText) 50%,transparent)}.topology-filter-input:hover{border-color:color-mix(in srgb,var(--tableText) 28%,transparent)}.topology-filter-input:focus{border-color:var(--primary);box-shadow:0 0 0 .1875rem color-mix(in srgb,var(--primary) 18%,transparent)}::ng-deep .topotip{display:none;position:fixed;background:var(--navigation);border-radius:.5rem;pointer-events:auto;box-shadow:0 .25rem 1rem 0 var(--shadow);color:var(--tableText);z-index:2000;padding:0;max-width:24rem;overflow:hidden}.topology-legend{width:15rem;border-radius:.5rem;position:absolute;bottom:1rem;right:.625rem;background:var(--navigation);box-shadow:0 .25rem .75rem 0 var(--shadow);padding:.9rem}.topology-legend.collapsed{width:auto}.legend-header{display:flex;align-items:center;justify-content:space-between;gap:.75rem;cursor:pointer;-webkit-user-select:none;user-select:none}.legend-header-title{font-family:Russo One,sans-serif;font-size:.9rem;text-transform:uppercase;letter-spacing:.05em;color:var(--primary)}.legend-header-toggle{color:var(--primary);display:flex;align-items:center}.legend-header-toggle svg{display:block;transition:transform .15s ease}.topology-legend.collapsed .legend-header-toggle svg{transform:rotate(180deg)}.legend-body{padding-top:.6rem}.legend-section{padding:.3rem 0}.legend-section:first-child{padding-top:0}.legend-section:last-child{padding-bottom:0;margin-bottom:-.5rem}.legend-section:not(:last-child){border-bottom:1px solid var(--shadow)}.legend-section-title{font-family:Russo One,sans-serif;font-size:.9rem;text-transform:uppercase;letter-spacing:.05em;color:var(--primary);margin-bottom:.25rem}.legend-item{display:flex;align-items:center;gap:0;padding:.225rem 0;cursor:help}.legend-icon-col{width:2.5rem;min-width:2.5rem;display:flex;align-items:center;justify-content:center;margin-right:.4rem}.legend-circle{height:.75rem;width:.75rem;min-width:.75rem;border-radius:50%}.legend-circle.online{background-color:#08dc5a}.legend-circle.offline{background-color:#8a8f9a}.legend-circle.unknown{background-color:#e6a700}.legend-circle.notenrolled{background-color:var(--background);border:.125rem solid #8a8f9a}.legend-line{height:.225rem;width:1.5rem}.legend-line.active-circuit{background-color:#00cd13}.legend-line.available{background:repeating-linear-gradient(to right,#8a8f9a 0,#8a8f9a .25rem,transparent .25rem,transparent .5rem)}.legend-line.broken{height:0;background:none;border-top:.28rem dotted #e6432e}.legend-text{font-size:.975rem;font-weight:500;color:var(--tableText);white-space:nowrap}.legend-node-icon{width:2rem;height:2rem;min-width:2rem;object-fit:contain}.legend-icon.misconfigured,.legend-icon.broken-link{width:1.2rem;height:1.2rem;min-width:1.2rem;background-image:url(/assets/images/visualizer/link-cut.png);background-size:contain;background-repeat:no-repeat}.legend-text2{display:inline-block;font-size:.625rem;font-weight:400;color:var(--tableText);padding-left:.125rem;padding-bottom:.25rem}@-webkit-keyframes rotating{0%{-webkit-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(360deg);-o-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes rotating{0%{-ms-transform:rotate(0deg);-moz-transform:rotate(0deg);-webkit-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0)}to{-ms-transform:rotate(360deg);-moz-transform:rotate(360deg);-webkit-transform:rotate(360deg);-o-transform:rotate(360deg);transform:rotate(360deg)}}.rotating{-webkit-animation:rotating 2s linear infinite;-moz-animation:rotating 2s linear infinite;-ms-animation:rotating 2s linear infinite;-o-animation:rotating 2s linear infinite;animation:rotating 2s linear infinite}::ng-deep .tool-tip-container{padding:0}::ng-deep .tool-tip-container .tooltip-header{font-family:Russo One,sans-serif;font-size:.975rem;color:var(--primary);padding:.7rem .9rem .15rem}::ng-deep .tool-tip-container .prop-row{display:flex;justify-content:space-between;gap:1.2rem;padding:.375rem .9rem;font-size:.9rem}::ng-deep .tool-tip-container .prop-row:nth-child(2n){background-color:var(--formBackground)}::ng-deep .tool-tip-container .prop-row:last-child{padding-bottom:.6rem}::ng-deep .tool-tip-container .prop-label{color:var(--tableText);opacity:.6;font-weight:500;white-space:nowrap}::ng-deep .tool-tip-container .prop-value{color:var(--tableText);font-weight:600;text-align:right}::ng-deep .tool-tip-container .tt-status-pill{display:inline-flex;align-items:center;gap:.35rem;margin:0 .9rem .55rem;padding:.14rem .55rem .14rem .45rem;border-radius:999px;font-size:.72rem;font-weight:600;letter-spacing:.02em}::ng-deep .tool-tip-container .tt-status-dot{width:.45rem;height:.45rem;min-width:.45rem;border-radius:50%}::ng-deep .tool-tip-container .prop-row:first-of-type{border-top:1px solid var(--shadow);padding-top:.5rem}::ng-deep .tool-tip-container .tt-ok{background:#08dc5a1f}::ng-deep .tool-tip-container .tt-ok .tt-status-dot{background:#08dc5a}::ng-deep .tool-tip-container .tt-ok .tt-status-label{color:#0a9d4a}::ng-deep .tool-tip-container .tt-broken{background:#e6432e1f}::ng-deep .tool-tip-container .tt-broken .tt-status-dot{background:#e6432e}::ng-deep .tool-tip-container .tt-broken .tt-status-label{color:#d23b27}::ng-deep .tool-tip-container .tt-offline{background:#8a8f9a29}::ng-deep .tool-tip-container .tt-offline .tt-status-dot{background:#8a8f9a}::ng-deep .tool-tip-container .tt-offline .tt-status-label{color:#6f7480}::ng-deep .tool-tip-container .tt-unknown{background:#e6a70024}::ng-deep .tool-tip-container .tt-unknown .tt-status-dot{background:#e6a700}::ng-deep .tool-tip-container .tt-unknown .tt-status-label{color:#b07f00}::ng-deep .tool-tip-container .tt-neutral{background:#6e78c81f}::ng-deep .tool-tip-container .tt-neutral .tt-status-dot{background:var(--primary)}::ng-deep .tool-tip-container .tt-neutral .tt-status-label{color:var(--primary)}::ng-deep .cdk-overlay-pane .mat-mdc-autocomplete-panel{border-radius:.75rem!important;background:var(--background);color:var(--tableText);padding:.35rem;border:1px solid var(--shadow);box-shadow:0 .5rem 1.5rem #00000029!important}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mdc-list-item{align-items:center;min-height:2.5rem;margin:.1rem 0;border-radius:.5rem;color:var(--tableText);transition:background-color .12s ease,color .12s ease}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mdc-list-item:hover:not(.mdc-list-item--disabled),::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mat-mdc-option-active:not(.mdc-list-item--disabled){background-color:color-mix(in srgb,var(--primary) 9%,transparent)!important}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mdc-list-item--selected:not(.mdc-list-item--disabled){background-color:color-mix(in srgb,var(--primary) 14%,transparent)!important}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mdc-list-item--selected .mdc-list-item__primary-text{color:var(--primary);font-weight:600}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .mat-mdc-option-ripple,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .mat-ripple{display:none}::ng-deep .mat-mdc-autocomplete-panel .mat-pseudo-checkbox-minimal{color:var(--primary)}::ng-deep .mat-mdc-raised-button .mdc-button__ripple{border:.1rem solid var(--primaryColor)}::ng-deep .p-button.p-button-icon-only{width:1.357rem}\n"] }]
|
|
12369
12761
|
}], ctorParameters: () => [{ type: IdentityServicePathHelper }, { type: MapDataService }, { type: SettingsService, decorators: [{
|
|
12370
12762
|
type: Inject,
|
|
12371
12763
|
args: [SETTINGS_SERVICE]
|
|
@@ -14218,7 +14610,7 @@ class ServicePolicyFormService {
|
|
|
14218
14610
|
});
|
|
14219
14611
|
}
|
|
14220
14612
|
getAssociatedServicesByRole(id) {
|
|
14221
|
-
this.zitiService.getSubdata('service-policies', id, 'services').then((result) => {
|
|
14613
|
+
this.zitiService.getSubdata('service-policies', id, 'services', this.zitiService.DEFAULT_PAGING).then((result) => {
|
|
14222
14614
|
this.associatedServices = result.data;
|
|
14223
14615
|
this.associatedServiceNames = this.associatedServices.map((svc) => {
|
|
14224
14616
|
return svc.name;
|
|
@@ -14226,7 +14618,7 @@ class ServicePolicyFormService {
|
|
|
14226
14618
|
});
|
|
14227
14619
|
}
|
|
14228
14620
|
getAssociatedIdentitiesById(id) {
|
|
14229
|
-
return this.zitiService.getSubdata('service-policies', id, 'identities').then((result) => {
|
|
14621
|
+
return this.zitiService.getSubdata('service-policies', id, 'identities', this.zitiService.DEFAULT_PAGING).then((result) => {
|
|
14230
14622
|
this.associatedIdentities = result.data;
|
|
14231
14623
|
this.associatedIdentityNames = this.associatedIdentities.map((ident) => {
|
|
14232
14624
|
return ident.name;
|
|
@@ -14858,11 +15250,11 @@ class PreviewListComponent {
|
|
|
14858
15250
|
return './' + hrefVal;
|
|
14859
15251
|
}
|
|
14860
15252
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PreviewListComponent, deps: [{ token: i4.Router }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
14861
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PreviewListComponent, isStandalone: false, selector: "lib-preview-list", inputs: { label: "label", clickable: "clickable", isLoading: "isLoading", allNames: "allNames", items: "items", allowRemove: "allowRemove", tooltip: "tooltip", showfilter: "showfilter", localFilter: "localFilter" }, outputs: { itemSelected: "itemSelected", itemRemoved: "itemRemoved", filterChanged: "filterChanged" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"listBox row\">\n @if (showfilter) {\n <input (keyup)=\"onKeydownEvent()\" [(ngModel)]=\"filterFor\" placeholder=\"Type to Filter\"/>\n }\n <div class=\"text row\">\n @if (previewItems.length <= 0) {\n <div class=\"listText\">\n <span class=\"preview-name preview-placeholder no-items-preview\">no items to display...</span>\n </div>\n }\n @for (item of previewItems; track item) {\n <div (click)=\"selected(item)\" [ngClass]=\"{ clickable: clickable }\" class=\"listText\">\n @if (allowRemove) {\n <div class=\"icon-clear\" (click)=\"remove(item)\"></div>\n }\n @if (!item.href) {\n <span class=\"preview-name\" matTooltip=\"{{tooltip}}\" matTooltipPosition=\"below\" matTooltipShowDelay=\"1000\">{{ item.name || item }}</span>\n }\n @if (item.href) {\n <a [href]=\"getHrefVal(item.href)\" (click)=\"linkClicked($event, item)\" class=\"{{item.linkClass ? item.linkClass : ''}}\" matTooltip=\"{{tooltip}}\" matTooltipShowDelay=\"1000\" matTooltipPosition=\"below\">\n @if (item.name) {\n <span class=\"preview-name\">{{ item.name }}@if (item.iconClass) {\n <div class=\"preview-item-icon {{item.iconClass}}\" (click)=\"remove(item)\"></div>\n }</span>\n }\n </a>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading) {\n <div class=\"spinner-container\">\n <span class=\"spinner\"></span>\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column;height:
|
|
15253
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PreviewListComponent, isStandalone: false, selector: "lib-preview-list", inputs: { label: "label", clickable: "clickable", isLoading: "isLoading", allNames: "allNames", items: "items", allowRemove: "allowRemove", tooltip: "tooltip", showfilter: "showfilter", localFilter: "localFilter" }, outputs: { itemSelected: "itemSelected", itemRemoved: "itemRemoved", filterChanged: "filterChanged" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"listBox row\">\n @if (showfilter) {\n <input (keyup)=\"onKeydownEvent()\" [(ngModel)]=\"filterFor\" placeholder=\"Type to Filter\"/>\n }\n <div class=\"text row\">\n @if (previewItems.length <= 0) {\n <div class=\"listText\">\n <span class=\"preview-name preview-placeholder no-items-preview\">no items to display...</span>\n </div>\n }\n @for (item of previewItems; track item) {\n <div (click)=\"selected(item)\" [ngClass]=\"{ clickable: clickable }\" class=\"listText\">\n @if (allowRemove) {\n <div class=\"icon-clear\" (click)=\"remove(item)\"></div>\n }\n @if (!item.href) {\n <span class=\"preview-name\" matTooltip=\"{{tooltip}}\" matTooltipPosition=\"below\" matTooltipShowDelay=\"1000\">{{ item.name || item }}</span>\n }\n @if (item.href) {\n <a [href]=\"getHrefVal(item.href)\" (click)=\"linkClicked($event, item)\" class=\"{{item.linkClass ? item.linkClass : ''}}\" matTooltip=\"{{tooltip}}\" matTooltipShowDelay=\"1000\" matTooltipPosition=\"below\">\n @if (item.name) {\n <span class=\"preview-name\">{{ item.name }}@if (item.iconClass) {\n <div class=\"preview-item-icon {{item.iconClass}}\" (click)=\"remove(item)\"></div>\n }</span>\n }\n </a>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading) {\n <div class=\"spinner-container\">\n <span class=\"spinner\"></span>\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column;height:auto;position:relative}.counter{background-color:#4c5566;color:var(--white);position:absolute;right:0rem}.listBox{background-color:var(--stroke);padding:.625rem;border-radius:var(--inputBorderRadius);height:auto;min-height:3.75rem}.listBox input{height:2.5rem;border-radius:var(--inputBorderRadius);font-size:.875rem;font-weight:600;background-image:url(/assets/images/Filter.png);background-size:auto 1.3125rem;background-position:top .5rem right .75rem;background-repeat:no-repeat;margin-bottom:.625rem;padding-left:.875rem}.text{overflow-y:auto;height:auto;max-height:12.5rem}.listText{width:100%;position:relative;float:left;font-size:.875rem;font-weight:600;padding-left:.625rem;padding-right:.625rem;margin-bottom:.125rem;color:var(--primary);display:flex;flex-direction:row;align-items:center;gap:.3125rem}.listText a{color:var(--primary);display:flex;flex-direction:row;align-items:center}.listText a:hover .preview-item-icon{text-decoration:underline}.listText .icon-clear{margin-top:.125rem}.listText .icon-clear:hover{color:var(--background)}::-webkit-scrollbar-thumb{background-color:var(--background)!important}.clickable{color:var(--primary);cursor:pointer}.clickable:hover,.clickable:focus{filter:brightness(90%)}.clickable:active{color:var(--icon);-webkit-transform:translateY(.0625rem);transform:translateY(.0625rem)}.clickable .preview-name:hover,.clickable .preview-name:focus{text-decoration:underline}.clickable .preview-name:active{text-decoration:underline}.preview-name{white-space:pre}.preview-name.preview-placeholder{font-style:italic}.preview-name.no-items-preview{font-weight:400}.preview-item-icon{margin-left:.4375rem;font-family:zac!important}.preview-more-results{margin-top:.3125rem}.preview-more-results .preview-name{font-weight:600;border-top-width:1px;text-transform:uppercase;font-size:.825rem;font-style:italic}.preview-more-results .preview-name:after{content:\"\\e939\";font-family:icomoon!important;font-style:normal}.spinner-container{position:absolute;display:flex;align-items:center;justify-content:center;width:100%;height:100%}.spinner-container .spinner{display:inline-block;width:2rem;height:2rem;border-radius:50%;background:transparent;border-top:.3125rem solid var(--tableText);border-right:.3125rem solid var(--tableText);border-bottom:.3125rem solid transparent;border-left:.3125rem solid transparent;-webkit-animation:loading .5s infinite linear;animation:loading .5s infinite linear;opacity:.7}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
|
|
14862
15254
|
}
|
|
14863
15255
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PreviewListComponent, decorators: [{
|
|
14864
15256
|
type: Component,
|
|
14865
|
-
args: [{ selector: 'lib-preview-list', standalone: false, template: "<div class=\"listBox row\">\n @if (showfilter) {\n <input (keyup)=\"onKeydownEvent()\" [(ngModel)]=\"filterFor\" placeholder=\"Type to Filter\"/>\n }\n <div class=\"text row\">\n @if (previewItems.length <= 0) {\n <div class=\"listText\">\n <span class=\"preview-name preview-placeholder no-items-preview\">no items to display...</span>\n </div>\n }\n @for (item of previewItems; track item) {\n <div (click)=\"selected(item)\" [ngClass]=\"{ clickable: clickable }\" class=\"listText\">\n @if (allowRemove) {\n <div class=\"icon-clear\" (click)=\"remove(item)\"></div>\n }\n @if (!item.href) {\n <span class=\"preview-name\" matTooltip=\"{{tooltip}}\" matTooltipPosition=\"below\" matTooltipShowDelay=\"1000\">{{ item.name || item }}</span>\n }\n @if (item.href) {\n <a [href]=\"getHrefVal(item.href)\" (click)=\"linkClicked($event, item)\" class=\"{{item.linkClass ? item.linkClass : ''}}\" matTooltip=\"{{tooltip}}\" matTooltipShowDelay=\"1000\" matTooltipPosition=\"below\">\n @if (item.name) {\n <span class=\"preview-name\">{{ item.name }}@if (item.iconClass) {\n <div class=\"preview-item-icon {{item.iconClass}}\" (click)=\"remove(item)\"></div>\n }</span>\n }\n </a>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading) {\n <div class=\"spinner-container\">\n <span class=\"spinner\"></span>\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column;height:
|
|
15257
|
+
args: [{ selector: 'lib-preview-list', standalone: false, template: "<div class=\"listBox row\">\n @if (showfilter) {\n <input (keyup)=\"onKeydownEvent()\" [(ngModel)]=\"filterFor\" placeholder=\"Type to Filter\"/>\n }\n <div class=\"text row\">\n @if (previewItems.length <= 0) {\n <div class=\"listText\">\n <span class=\"preview-name preview-placeholder no-items-preview\">no items to display...</span>\n </div>\n }\n @for (item of previewItems; track item) {\n <div (click)=\"selected(item)\" [ngClass]=\"{ clickable: clickable }\" class=\"listText\">\n @if (allowRemove) {\n <div class=\"icon-clear\" (click)=\"remove(item)\"></div>\n }\n @if (!item.href) {\n <span class=\"preview-name\" matTooltip=\"{{tooltip}}\" matTooltipPosition=\"below\" matTooltipShowDelay=\"1000\">{{ item.name || item }}</span>\n }\n @if (item.href) {\n <a [href]=\"getHrefVal(item.href)\" (click)=\"linkClicked($event, item)\" class=\"{{item.linkClass ? item.linkClass : ''}}\" matTooltip=\"{{tooltip}}\" matTooltipShowDelay=\"1000\" matTooltipPosition=\"below\">\n @if (item.name) {\n <span class=\"preview-name\">{{ item.name }}@if (item.iconClass) {\n <div class=\"preview-item-icon {{item.iconClass}}\" (click)=\"remove(item)\"></div>\n }</span>\n }\n </a>\n }\n </div>\n }\n </div>\n</div>\n@if (isLoading) {\n <div class=\"spinner-container\">\n <span class=\"spinner\"></span>\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column;height:auto;position:relative}.counter{background-color:#4c5566;color:var(--white);position:absolute;right:0rem}.listBox{background-color:var(--stroke);padding:.625rem;border-radius:var(--inputBorderRadius);height:auto;min-height:3.75rem}.listBox input{height:2.5rem;border-radius:var(--inputBorderRadius);font-size:.875rem;font-weight:600;background-image:url(/assets/images/Filter.png);background-size:auto 1.3125rem;background-position:top .5rem right .75rem;background-repeat:no-repeat;margin-bottom:.625rem;padding-left:.875rem}.text{overflow-y:auto;height:auto;max-height:12.5rem}.listText{width:100%;position:relative;float:left;font-size:.875rem;font-weight:600;padding-left:.625rem;padding-right:.625rem;margin-bottom:.125rem;color:var(--primary);display:flex;flex-direction:row;align-items:center;gap:.3125rem}.listText a{color:var(--primary);display:flex;flex-direction:row;align-items:center}.listText a:hover .preview-item-icon{text-decoration:underline}.listText .icon-clear{margin-top:.125rem}.listText .icon-clear:hover{color:var(--background)}::-webkit-scrollbar-thumb{background-color:var(--background)!important}.clickable{color:var(--primary);cursor:pointer}.clickable:hover,.clickable:focus{filter:brightness(90%)}.clickable:active{color:var(--icon);-webkit-transform:translateY(.0625rem);transform:translateY(.0625rem)}.clickable .preview-name:hover,.clickable .preview-name:focus{text-decoration:underline}.clickable .preview-name:active{text-decoration:underline}.preview-name{white-space:pre}.preview-name.preview-placeholder{font-style:italic}.preview-name.no-items-preview{font-weight:400}.preview-item-icon{margin-left:.4375rem;font-family:zac!important}.preview-more-results{margin-top:.3125rem}.preview-more-results .preview-name{font-weight:600;border-top-width:1px;text-transform:uppercase;font-size:.825rem;font-style:italic}.preview-more-results .preview-name:after{content:\"\\e939\";font-family:icomoon!important;font-style:normal}.spinner-container{position:absolute;display:flex;align-items:center;justify-content:center;width:100%;height:100%}.spinner-container .spinner{display:inline-block;width:2rem;height:2rem;border-radius:50%;background:transparent;border-top:.3125rem solid var(--tableText);border-right:.3125rem solid var(--tableText);border-bottom:.3125rem solid transparent;border-left:.3125rem solid transparent;-webkit-animation:loading .5s infinite linear;animation:loading .5s infinite linear;opacity:.7}\n"] }]
|
|
14866
15258
|
}], ctorParameters: () => [{ type: i4.Router }], propDecorators: { label: [{
|
|
14867
15259
|
type: Input
|
|
14868
15260
|
}], clickable: [{
|
|
@@ -15620,7 +16012,7 @@ class IdentityFormComponent extends ProjectableForm {
|
|
|
15620
16012
|
});
|
|
15621
16013
|
}
|
|
15622
16014
|
getAssociatedServices() {
|
|
15623
|
-
this.zitiService.getSubdata('identities', this.formData.id, 'services').then((result) => {
|
|
16015
|
+
this.zitiService.getSubdata('identities', this.formData.id, 'services', this.zitiService.DEFAULT_PAGING).then((result) => {
|
|
15624
16016
|
this.associatedServices = result.data;
|
|
15625
16017
|
this.associatedServiceNames = this.associatedServices.map((svc) => {
|
|
15626
16018
|
return svc.name;
|
|
@@ -15628,7 +16020,7 @@ class IdentityFormComponent extends ProjectableForm {
|
|
|
15628
16020
|
});
|
|
15629
16021
|
}
|
|
15630
16022
|
getAssociatedServicePolicies() {
|
|
15631
|
-
this.zitiService.getSubdata('identities', this.formData.id, 'service-policies').then((result) => {
|
|
16023
|
+
this.zitiService.getSubdata('identities', this.formData.id, 'service-policies', this.zitiService.DEFAULT_PAGING).then((result) => {
|
|
15632
16024
|
this.associatedServicePolicies = result.data;
|
|
15633
16025
|
this.associatedServicePolicyNames = this.associatedServicePolicies.map((policy) => {
|
|
15634
16026
|
return policy.name;
|
|
@@ -19487,7 +19879,7 @@ class TransitRouterFormService {
|
|
|
19487
19879
|
this.growlerService.show(growlerData);
|
|
19488
19880
|
}
|
|
19489
19881
|
getAssociatedServices(id) {
|
|
19490
|
-
this.zitiService.getSubdata('transit-routers', id, 'services').then((result) => {
|
|
19882
|
+
this.zitiService.getSubdata('transit-routers', id, 'services', this.zitiService.DEFAULT_PAGING).then((result) => {
|
|
19491
19883
|
this.associatedServices = result.data;
|
|
19492
19884
|
this.associatedServiceNames = this.associatedServices.map((svc) => {
|
|
19493
19885
|
return svc.name;
|
|
@@ -19495,7 +19887,7 @@ class TransitRouterFormService {
|
|
|
19495
19887
|
});
|
|
19496
19888
|
}
|
|
19497
19889
|
getAssociatedIdentities(id) {
|
|
19498
|
-
this.zitiService.getSubdata('transit-routers', id, 'identities').then((result) => {
|
|
19890
|
+
this.zitiService.getSubdata('transit-routers', id, 'identities', this.zitiService.DEFAULT_PAGING).then((result) => {
|
|
19499
19891
|
this.associatedIdentities = result.data;
|
|
19500
19892
|
this.associatedIdentityNames = this.associatedIdentities.map((policy) => {
|
|
19501
19893
|
return policy.name;
|
|
@@ -20989,7 +21381,9 @@ class SimpleServiceComponent extends ProjectableForm {
|
|
|
20989
21381
|
this.identityNamedAttributes = namedAttributes;
|
|
20990
21382
|
this.mergeSelectedIdentityNamesIntoAvailableList();
|
|
20991
21383
|
}).finally(() => {
|
|
20992
|
-
this.identitiesInit.
|
|
21384
|
+
if (!this.identitiesInit.value) {
|
|
21385
|
+
this.identitiesInit.next(true);
|
|
21386
|
+
}
|
|
20993
21387
|
});
|
|
20994
21388
|
}
|
|
20995
21389
|
checkAllDataInit() {
|
|
@@ -23293,7 +23687,7 @@ class EdgeRouterPolicyFormService {
|
|
|
23293
23687
|
});
|
|
23294
23688
|
}
|
|
23295
23689
|
getAssociatedServicesByRole(id) {
|
|
23296
|
-
this.zitiService.getSubdata('edge-router-policies', id, 'edge-routers').then((result) => {
|
|
23690
|
+
this.zitiService.getSubdata('edge-router-policies', id, 'edge-routers', this.zitiService.DEFAULT_PAGING).then((result) => {
|
|
23297
23691
|
this.associatedEdgeRouters = result.data;
|
|
23298
23692
|
this.associatedEdgeRouterNames = this.associatedEdgeRouters.map((svc) => {
|
|
23299
23693
|
return svc.name;
|
|
@@ -23301,7 +23695,7 @@ class EdgeRouterPolicyFormService {
|
|
|
23301
23695
|
});
|
|
23302
23696
|
}
|
|
23303
23697
|
getAssociatedIdentitiesById(id) {
|
|
23304
|
-
this.zitiService.getSubdata('edge-router-policies', id, 'identities').then((result) => {
|
|
23698
|
+
this.zitiService.getSubdata('edge-router-policies', id, 'identities', this.zitiService.DEFAULT_PAGING).then((result) => {
|
|
23305
23699
|
this.associatedIdentities = result.data;
|
|
23306
23700
|
this.associatedIdentityNames = this.associatedIdentities.map((ident) => {
|
|
23307
23701
|
return ident.name;
|
|
@@ -24963,7 +25357,7 @@ class ConfigurationService {
|
|
|
24963
25357
|
});
|
|
24964
25358
|
}
|
|
24965
25359
|
getAssociatedServices(configId) {
|
|
24966
|
-
return this.dataService.getSubdata('configs', configId, 'services').then((result) => {
|
|
25360
|
+
return this.dataService.getSubdata('configs', configId, 'services', this.dataService.DEFAULT_PAGING).then((result) => {
|
|
24967
25361
|
const associatedServices = result.data;
|
|
24968
25362
|
const associatedServiceNames = associatedServices.map((svc) => {
|
|
24969
25363
|
return svc.name;
|
|
@@ -30703,7 +31097,7 @@ class APISessionFormService {
|
|
|
30703
31097
|
this.growlerService.show(growlerData);
|
|
30704
31098
|
}
|
|
30705
31099
|
getAssociatedServices(id) {
|
|
30706
|
-
this.zitiService.getSubdata('transit-routers', id, 'services').then((result) => {
|
|
31100
|
+
this.zitiService.getSubdata('transit-routers', id, 'services', this.zitiService.DEFAULT_PAGING).then((result) => {
|
|
30707
31101
|
this.associatedServices = result.data;
|
|
30708
31102
|
this.associatedServiceNames = this.associatedServices.map((svc) => {
|
|
30709
31103
|
return svc.name;
|
|
@@ -30711,7 +31105,7 @@ class APISessionFormService {
|
|
|
30711
31105
|
});
|
|
30712
31106
|
}
|
|
30713
31107
|
getAssociatedIdentities(id) {
|
|
30714
|
-
this.zitiService.getSubdata('transit-routers', id, 'identities').then((result) => {
|
|
31108
|
+
this.zitiService.getSubdata('transit-routers', id, 'identities', this.zitiService.DEFAULT_PAGING).then((result) => {
|
|
30715
31109
|
this.associatedIdentities = result.data;
|
|
30716
31110
|
this.associatedIdentityNames = this.associatedIdentities.map((policy) => {
|
|
30717
31111
|
return policy.name;
|
|
@@ -40599,7 +40993,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
40599
40993
|
}] }, { type: ZitiOidcService }, { type: GrowlerService }, { type: i0.Injector }] });
|
|
40600
40994
|
|
|
40601
40995
|
const ZAC_VERSION = {
|
|
40602
|
-
"version": "4.3.
|
|
40996
|
+
"version": "4.3.1"
|
|
40603
40997
|
};
|
|
40604
40998
|
|
|
40605
40999
|
/*
|