sovereign 1.0.0b123__py3-none-any.whl → 1.0.0b134__py3-none-any.whl
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.
- sovereign/app.py +1 -1
- sovereign/cache/__init__.py +182 -0
- sovereign/cache/backends/__init__.py +110 -0
- sovereign/cache/backends/s3.py +139 -0
- sovereign/cache/filesystem.py +42 -0
- sovereign/cache/types.py +15 -0
- sovereign/context.py +20 -18
- sovereign/events.py +49 -0
- sovereign/middlewares.py +1 -1
- sovereign/rendering.py +74 -35
- sovereign/schemas.py +112 -110
- sovereign/server.py +4 -3
- sovereign/sources/poller.py +20 -4
- sovereign/statistics.py +1 -1
- sovereign/templates/base.html +59 -46
- sovereign/templates/resources.html +40 -835
- sovereign/utils/mock.py +7 -3
- sovereign/views/healthchecks.py +1 -1
- sovereign/views/interface.py +34 -15
- sovereign/worker.py +87 -46
- {sovereign-1.0.0b123.dist-info → sovereign-1.0.0b134.dist-info}/METADATA +4 -5
- {sovereign-1.0.0b123.dist-info → sovereign-1.0.0b134.dist-info}/RECORD +33 -24
- {sovereign-1.0.0b123.dist-info → sovereign-1.0.0b134.dist-info}/WHEEL +1 -1
- {sovereign-1.0.0b123.dist-info → sovereign-1.0.0b134.dist-info}/entry_points.txt +3 -0
- sovereign_files/__init__.py +0 -0
- sovereign_files/static/darkmode.js +51 -0
- sovereign_files/static/node_expression.js +42 -0
- sovereign_files/static/resources.css +246 -0
- sovereign_files/static/resources.js +642 -0
- sovereign_files/static/sass/style.scss +33 -0
- sovereign_files/static/style.css +16143 -0
- sovereign_files/static/style.css.map +1 -0
- sovereign/cache.py +0 -133
- sovereign/static/node_expression.js +0 -16
- sovereign/static/sass/style.scss +0 -27
- sovereign/static/style.css +0 -13553
- sovereign-1.0.0b123.dist-info/LICENSE.txt +0 -13
- {sovereign → sovereign_files}/static/panel.js +0 -0
|
@@ -5,278 +5,28 @@
|
|
|
5
5
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
6
6
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
7
7
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
{% for resource in resources %}
|
|
12
|
-
"{{ resource.name or resource.cluster_name or 'Unknown' }}"{% if not loop.last %},{% endif %}
|
|
13
|
-
{% endfor %}
|
|
14
|
-
];
|
|
15
|
-
const resources = resourceNames.map((name, index) => ({ name: name, index: index }));
|
|
16
|
-
let filteredResources = [...resources];
|
|
17
|
-
|
|
18
|
-
// Function to set envoy_version cookie and reload page
|
|
19
|
-
function setEnvoyVersion(version) {
|
|
20
|
-
document.cookie = `envoy_version=${version}; path=/ui/resources/; max-age=31536000`;
|
|
21
|
-
window.location.reload();
|
|
22
|
-
}
|
|
23
|
-
</script>
|
|
24
|
-
<style>
|
|
25
|
-
#filterInput {
|
|
26
|
-
font-family: monospace;
|
|
27
|
-
}
|
|
28
|
-
.filtered {
|
|
29
|
-
display: none;
|
|
30
|
-
}
|
|
31
|
-
.tooltip {
|
|
32
|
-
position: relative;
|
|
33
|
-
display: inline-block;
|
|
34
|
-
cursor: help;
|
|
35
|
-
}
|
|
36
|
-
.tooltip .tooltip-text {
|
|
37
|
-
visibility: hidden;
|
|
38
|
-
width: 220px;
|
|
39
|
-
background-color: #363636;
|
|
40
|
-
color: #fff;
|
|
41
|
-
text-align: left;
|
|
42
|
-
padding: 0.5rem;
|
|
43
|
-
border-radius: 6px;
|
|
44
|
-
position: absolute;
|
|
45
|
-
z-index: 1;
|
|
46
|
-
top: 125%;
|
|
47
|
-
left: 50%;
|
|
48
|
-
margin-left: -110px;
|
|
49
|
-
opacity: 0;
|
|
50
|
-
transition: opacity 0.3s;
|
|
51
|
-
font-size: 0.875rem;
|
|
52
|
-
}
|
|
53
|
-
.tooltip:hover .tooltip-text {
|
|
54
|
-
visibility: visible;
|
|
55
|
-
opacity: 1;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/* Side panel styles - using Bulma-compatible approach */
|
|
59
|
-
.side-panel {
|
|
60
|
-
position: fixed;
|
|
61
|
-
top: 0;
|
|
62
|
-
right: -100%;
|
|
63
|
-
width: 95%;
|
|
64
|
-
max-width: 1200px;
|
|
65
|
-
min-width: 400px;
|
|
66
|
-
height: 100vh;
|
|
67
|
-
z-index: 1000;
|
|
68
|
-
transition: right 0.3s ease-in-out;
|
|
69
|
-
overflow-y: auto;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.side-panel.is-active {
|
|
73
|
-
right: 0;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/* Responsive side panel sizing */
|
|
77
|
-
@media (min-width: 768px) {
|
|
78
|
-
.side-panel { width: 75%; right: -75%; }
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
@media (min-width: 1024px) {
|
|
82
|
-
.side-panel { width: 65%; right: -65%; }
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
@media (min-width: 1400px) {
|
|
86
|
-
.side-panel { width: 55%; right: -55%; }
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
@media (min-width: 1600px) {
|
|
90
|
-
.side-panel { width: 50%; right: -50%; }
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
@media (max-width: 480px) {
|
|
94
|
-
.side-panel { width: 95%; min-width: 300px; right: -95%; }
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
.json-container {
|
|
98
|
-
position: relative;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
.json-header {
|
|
102
|
-
padding: 1rem;
|
|
103
|
-
border-bottom: 1px solid #555;
|
|
104
|
-
background-color: #2d2d2d;
|
|
105
|
-
display: flex;
|
|
106
|
-
justify-content: space-between;
|
|
107
|
-
align-items: center;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
.json-content {
|
|
112
|
-
background-color: #1e1e1e;
|
|
113
|
-
border: none;
|
|
114
|
-
border-radius: 0;
|
|
115
|
-
padding: 1rem;
|
|
116
|
-
font-family: 'JetBrains Mono', 'Fira Code', 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', 'Source Code Pro', 'Menlo', 'Consolas', monospace;
|
|
117
|
-
font-size: 0.875rem;
|
|
118
|
-
line-height: 1.5;
|
|
119
|
-
white-space: pre-wrap;
|
|
120
|
-
word-wrap: break-word;
|
|
121
|
-
max-height: calc(100vh - 250px);
|
|
122
|
-
overflow-y: auto;
|
|
123
|
-
color: #d4d4d4;
|
|
124
|
-
margin: 0;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/* JSON Syntax Highlighting - VS Code Dark+ theme colors */
|
|
128
|
-
.json-key {
|
|
129
|
-
color: #9cdcfe;
|
|
130
|
-
font-weight: 400;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
.json-string {
|
|
134
|
-
color: #ce9178;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
.json-number {
|
|
138
|
-
color: #b5cea8;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
.json-boolean {
|
|
142
|
-
color: #569cd6;
|
|
143
|
-
font-weight: 500;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
.json-null {
|
|
147
|
-
color: #569cd6;
|
|
148
|
-
font-weight: 500;
|
|
149
|
-
font-style: italic;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
.json-punctuation {
|
|
153
|
-
color: #d4d4d4;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/* Improve readability */
|
|
157
|
-
.json-content::-webkit-scrollbar {
|
|
158
|
-
width: 8px;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
.json-content::-webkit-scrollbar-track {
|
|
162
|
-
background: #2d2d2d;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
.json-content::-webkit-scrollbar-thumb {
|
|
166
|
-
background: #555;
|
|
167
|
-
border-radius: 4px;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
.json-content::-webkit-scrollbar-thumb:hover {
|
|
171
|
-
background: #666;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/* Collapsible JSON */
|
|
175
|
-
.json-toggle {
|
|
176
|
-
cursor: pointer;
|
|
177
|
-
user-select: none;
|
|
178
|
-
color: #808080;
|
|
179
|
-
margin-right: 0.25rem;
|
|
180
|
-
font-family: monospace;
|
|
181
|
-
font-size: 0.875rem;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
.json-toggle:hover {
|
|
185
|
-
color: #fff;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
.json-collapsible {
|
|
189
|
-
margin-left: 1rem;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
.json-collapsed {
|
|
193
|
-
display: none;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
.loading-spinner {
|
|
197
|
-
display: inline-block;
|
|
198
|
-
width: 20px;
|
|
199
|
-
height: 20px;
|
|
200
|
-
border: 3px solid #f3f3f3;
|
|
201
|
-
border-top: 3px solid #433fca;
|
|
202
|
-
border-radius: 50%;
|
|
203
|
-
animation: spin 1s linear infinite;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
@keyframes spin {
|
|
207
|
-
0% { transform: rotate(0deg); }
|
|
208
|
-
100% { transform: rotate(360deg); }
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/* Prevent body scroll when side panel is open */
|
|
212
|
-
body.side-panel-open {
|
|
213
|
-
overflow: hidden;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/* Backdrop overlay for side panel */
|
|
217
|
-
.side-panel-backdrop {
|
|
218
|
-
position: fixed;
|
|
219
|
-
top: 0;
|
|
220
|
-
left: 0;
|
|
221
|
-
width: 100%;
|
|
222
|
-
height: 100%;
|
|
223
|
-
background-color: rgba(0, 0, 0, 0.5);
|
|
224
|
-
z-index: 999;
|
|
225
|
-
opacity: 0;
|
|
226
|
-
visibility: hidden;
|
|
227
|
-
transition: opacity 0.3s ease-in-out, visibility 0.3s ease-in-out;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
.side-panel-backdrop.is-active {
|
|
231
|
-
opacity: 1;
|
|
232
|
-
visibility: visible;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/* Show backdrop on smaller screens */
|
|
236
|
-
@media (max-width: 1024px) {
|
|
237
|
-
.side-panel-backdrop.is-active {
|
|
238
|
-
opacity: 1;
|
|
239
|
-
visibility: visible;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/* Minimal custom styles - using Bulma classes for most styling */
|
|
244
|
-
.json-chunk-controls {
|
|
245
|
-
background-color: #2d2d2d;
|
|
246
|
-
border-bottom: 1px solid #555;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/* Preferences details styling */
|
|
250
|
-
details summary {
|
|
251
|
-
list-style: none;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
details summary::-webkit-details-marker {
|
|
255
|
-
display: none;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
details summary::before {
|
|
259
|
-
content: "▶";
|
|
260
|
-
margin-right: 0.5rem;
|
|
261
|
-
transition: transform 0.2s;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
details[open] summary::before {
|
|
265
|
-
transform: rotate(90deg);
|
|
266
|
-
}
|
|
267
|
-
</style>
|
|
268
|
-
|
|
8
|
+
<link rel="stylesheet" href="/static/resources.css">
|
|
9
|
+
<script src="/static/resources.js"></script>
|
|
10
|
+
<script src="/static/panel.js"></script>
|
|
269
11
|
{% endblock %}
|
|
270
12
|
|
|
271
13
|
|
|
272
14
|
{%- block body %}
|
|
273
15
|
<div class="content">
|
|
16
|
+
{% if error %}
|
|
17
|
+
<span class="panel-icon">
|
|
18
|
+
<i class="fas fa-arrow-right" aria-hidden="true"></i>
|
|
19
|
+
</span>
|
|
20
|
+
<div class="notification is-danger">
|
|
21
|
+
{{ error }}
|
|
22
|
+
</div>
|
|
23
|
+
{% endif %}
|
|
274
24
|
<p class="content">
|
|
275
25
|
<div class="columns">
|
|
276
26
|
<div class="column is-narrow">
|
|
277
27
|
<div class="dropdown is-hoverable">
|
|
278
28
|
<div class="dropdown-trigger">
|
|
279
|
-
<button class="button is-
|
|
29
|
+
<button class="button is-primary" aria-haspopup="true" aria-controls="dropdown-menu">
|
|
280
30
|
<span>Template Version</span>
|
|
281
31
|
<span class="icon is-small">
|
|
282
32
|
<i class="fas fa-angle-down" aria-hidden="false"></i>
|
|
@@ -301,8 +51,9 @@
|
|
|
301
51
|
<!-- node expression box -->
|
|
302
52
|
<div class="column">
|
|
303
53
|
<form id="filterForm">
|
|
304
|
-
<input id="filterInput" class="input
|
|
54
|
+
<input id="filterInput" class="input" type="text" placeholder="Node filter expression"/>
|
|
305
55
|
</form>
|
|
56
|
+
<p id="filterMessage" class="help"></p>
|
|
306
57
|
</div>
|
|
307
58
|
<div class="column is-narrow">
|
|
308
59
|
<div class="tooltip">
|
|
@@ -322,9 +73,10 @@
|
|
|
322
73
|
</div>
|
|
323
74
|
</div>
|
|
324
75
|
|
|
76
|
+
|
|
325
77
|
{% set count = resources|length %}
|
|
326
78
|
{% if count > 0 %}
|
|
327
|
-
<nav class="panel is-
|
|
79
|
+
<nav class="panel is-primary" id="resources">
|
|
328
80
|
<p class="panel-heading">
|
|
329
81
|
{{ resource_type|capitalize }}
|
|
330
82
|
</p>
|
|
@@ -361,14 +113,19 @@
|
|
|
361
113
|
</p>
|
|
362
114
|
</div>
|
|
363
115
|
</nav>
|
|
116
|
+
{% if count < 10 %}
|
|
117
|
+
{% set hidden = "display: none;" %}
|
|
118
|
+
{% endif %}
|
|
119
|
+
<div style="{{ hidden }}">
|
|
364
120
|
<nav class="pagination is-right is-small" role="navigation" aria-label="pagination">
|
|
365
121
|
<a id="prev-btn" class="pagination-previous">Previous</a>
|
|
366
122
|
<a id="next-btn" class="pagination-next">Next</a>
|
|
367
123
|
<ul id="page-numbers" class="pagination-list"></ul>
|
|
368
124
|
</nav>
|
|
125
|
+
</div>
|
|
369
126
|
|
|
370
127
|
{% if resource_type == 'routes' %}
|
|
371
|
-
<nav class="panel">
|
|
128
|
+
<nav class="panel is-primary">
|
|
372
129
|
<p class="panel-heading">Virtual Hosts</p>
|
|
373
130
|
<div class="panel-block">
|
|
374
131
|
<p class="control has-icons-left">
|
|
@@ -393,6 +150,9 @@
|
|
|
393
150
|
data-category="{{ resource['name'] }}"
|
|
394
151
|
href="#"
|
|
395
152
|
onclick="loadVirtualHostInSidePanel('{{ resource['name'] }}', '{{ virtualhost['name'] }}'); return false;">
|
|
153
|
+
<p class="tag is-small is-primary">
|
|
154
|
+
{{ resource['name'] }}
|
|
155
|
+
</p>
|
|
396
156
|
<span class="panel-icon">
|
|
397
157
|
<i class="fas fa-arrow-right" aria-hidden="true"></i>
|
|
398
158
|
</span>
|
|
@@ -406,7 +166,7 @@
|
|
|
406
166
|
<div class="notification is-danger">
|
|
407
167
|
No resources found
|
|
408
168
|
<script>
|
|
409
|
-
setTimeout(() => location.reload(),
|
|
169
|
+
setTimeout(() => location.reload(), 6000);
|
|
410
170
|
</script>
|
|
411
171
|
</div>
|
|
412
172
|
{% endif %}
|
|
@@ -480,576 +240,21 @@
|
|
|
480
240
|
{% block footer %}
|
|
481
241
|
<div class="content has-text-centered is-small">
|
|
482
242
|
<script src="/static/node_expression.js"></script>
|
|
483
|
-
<script src="/static/panel.js"></script>
|
|
484
243
|
<script>
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
document.getElementById('sidePanelContent').innerHTML = `
|
|
500
|
-
<div style="text-align: center; padding: 2rem;">
|
|
501
|
-
<div class="loading-spinner"></div>
|
|
502
|
-
<p style="margin-top: 1rem;">Loading resource data...</p>
|
|
503
|
-
</div>
|
|
504
|
-
`;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
function showError(message) {
|
|
508
|
-
document.getElementById('sidePanelContent').innerHTML = `
|
|
509
|
-
<div class="notification is-danger">
|
|
510
|
-
<strong>Error:</strong> ${message}
|
|
511
|
-
</div>
|
|
512
|
-
`;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
function escapeHtml(text) {
|
|
516
|
-
const div = document.createElement('div');
|
|
517
|
-
div.textContent = text;
|
|
518
|
-
return div.innerHTML;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
function syntaxHighlight(json) {
|
|
522
|
-
if (typeof json !== 'string') {
|
|
523
|
-
json = JSON.stringify(json, null, 2);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
// Escape HTML first
|
|
527
|
-
json = escapeHtml(json);
|
|
528
|
-
|
|
529
|
-
// Apply syntax highlighting
|
|
530
|
-
json = json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
|
|
531
|
-
let cls = 'json-number';
|
|
532
|
-
if (/^"/.test(match)) {
|
|
533
|
-
if (/:$/.test(match)) {
|
|
534
|
-
cls = 'json-key';
|
|
535
|
-
// Remove the colon from the match for styling, we'll add it back
|
|
536
|
-
match = match.slice(0, -1);
|
|
537
|
-
return '<span class="' + cls + '">' + match + '</span><span class="json-punctuation">:</span>';
|
|
538
|
-
} else {
|
|
539
|
-
cls = 'json-string';
|
|
540
|
-
}
|
|
541
|
-
} else if (/true|false/.test(match)) {
|
|
542
|
-
cls = 'json-boolean';
|
|
543
|
-
} else if (/null/.test(match)) {
|
|
544
|
-
cls = 'json-null';
|
|
545
|
-
}
|
|
546
|
-
return '<span class="' + cls + '">' + match + '</span>';
|
|
547
|
-
});
|
|
548
|
-
|
|
549
|
-
// Highlight punctuation (brackets, braces, commas)
|
|
550
|
-
json = json.replace(/([{}[\],])/g, '<span class="json-punctuation">$1</span>');
|
|
551
|
-
|
|
552
|
-
return json;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
function makeJsonCollapsible(element) {
|
|
556
|
-
// This function would add collapsible functionality
|
|
557
|
-
// For now, we'll keep it simple and just return the element
|
|
558
|
-
// Future enhancement: add click handlers for { } and [ ] to collapse/expand
|
|
559
|
-
return element;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
function copyToClipboard(text) {
|
|
563
|
-
if (navigator.clipboard && window.isSecureContext) {
|
|
564
|
-
// Use the modern clipboard API
|
|
565
|
-
navigator.clipboard.writeText(text).then(() => {
|
|
566
|
-
showCopySuccess();
|
|
567
|
-
}).catch(err => {
|
|
568
|
-
console.error('Failed to copy: ', err);
|
|
569
|
-
fallbackCopyTextToClipboard(text);
|
|
570
|
-
});
|
|
571
|
-
} else {
|
|
572
|
-
// Fallback for older browsers
|
|
573
|
-
fallbackCopyTextToClipboard(text);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
function fallbackCopyTextToClipboard(text) {
|
|
578
|
-
const textArea = document.createElement("textarea");
|
|
579
|
-
textArea.value = text;
|
|
580
|
-
textArea.style.top = "0";
|
|
581
|
-
textArea.style.left = "0";
|
|
582
|
-
textArea.style.position = "fixed";
|
|
583
|
-
document.body.appendChild(textArea);
|
|
584
|
-
textArea.focus();
|
|
585
|
-
textArea.select();
|
|
586
|
-
|
|
587
|
-
try {
|
|
588
|
-
const successful = document.execCommand('copy');
|
|
589
|
-
if (successful) {
|
|
590
|
-
showCopySuccess();
|
|
591
|
-
}
|
|
592
|
-
} catch (err) {
|
|
593
|
-
console.error('Fallback: Oops, unable to copy', err);
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
document.body.removeChild(textArea);
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
function showCopySuccess() {
|
|
600
|
-
const copyBtn = document.querySelector('.copy-button');
|
|
601
|
-
if (copyBtn) {
|
|
602
|
-
const originalText = copyBtn.textContent;
|
|
603
|
-
copyBtn.textContent = 'Copied!';
|
|
604
|
-
copyBtn.style.backgroundColor = '#48c774';
|
|
605
|
-
setTimeout(() => {
|
|
606
|
-
copyBtn.textContent = originalText;
|
|
607
|
-
copyBtn.style.backgroundColor = '#433fca';
|
|
608
|
-
}, 2000);
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
// Global variables for chunked loading
|
|
613
|
-
let currentResourceData = null;
|
|
614
|
-
let currentChunkSize = 1000; // lines
|
|
615
|
-
let currentChunkIndex = 0;
|
|
616
|
-
let totalChunks = 0;
|
|
617
|
-
|
|
618
|
-
// Helper function to generate preferences section HTML
|
|
619
|
-
function getPreferencesHtml() {
|
|
620
|
-
const currentThreshold = localStorage.getItem('jsonSizeThreshold') || 0.5;
|
|
621
|
-
const autoChunk = localStorage.getItem('autoChunkLargeJson') === 'true';
|
|
622
|
-
|
|
623
|
-
return `
|
|
624
|
-
<details class="has-background-grey-darker">
|
|
625
|
-
<summary class="has-background-grey-dark has-text-white-ter p-3 is-clickable" style="cursor: pointer;">
|
|
626
|
-
<span class="is-size-7">Display Preferences</span>
|
|
627
|
-
</summary>
|
|
628
|
-
<div class="box has-background-grey-dark has-text-white-ter m-0" style="border-radius: 0;">
|
|
629
|
-
<div class="content is-small">
|
|
630
|
-
<div class="field">
|
|
631
|
-
<label class="checkbox has-text-white-ter">
|
|
632
|
-
<input type="checkbox" id="autoChunkPreference" ${autoChunk ? 'checked' : ''}
|
|
633
|
-
onchange="event.stopPropagation(); updateAutoChunkPreference(this.checked)"
|
|
634
|
-
class="mr-2">
|
|
635
|
-
Always use chunked view for large files
|
|
636
|
-
</label>
|
|
637
|
-
</div>
|
|
638
|
-
<div class="field is-grouped">
|
|
639
|
-
<div class="control">
|
|
640
|
-
<label class="label is-small has-text-white-ter">Size threshold:</label>
|
|
641
|
-
</div>
|
|
642
|
-
<div class="control">
|
|
643
|
-
<div class="select is-dark is-small">
|
|
644
|
-
<select id="thresholdSelector" value="${currentThreshold}"
|
|
645
|
-
onchange="event.stopPropagation(); updateThresholdPreference(this.value)">
|
|
646
|
-
<option value="0.25" ${currentThreshold == 0.25 ? 'selected' : ''}>250 KB</option>
|
|
647
|
-
<option value="0.5" ${currentThreshold == 0.5 ? 'selected' : ''}>500 KB</option>
|
|
648
|
-
<option value="1" ${currentThreshold == 1 ? 'selected' : ''}>1 MB</option>
|
|
649
|
-
<option value="2" ${currentThreshold == 2 ? 'selected' : ''}>2 MB</option>
|
|
650
|
-
<option value="5" ${currentThreshold == 5 ? 'selected' : ''}>5 MB</option>
|
|
651
|
-
</select>
|
|
652
|
-
</div>
|
|
653
|
-
</div>
|
|
654
|
-
</div>
|
|
655
|
-
<div class="field is-grouped">
|
|
656
|
-
<div class="control">
|
|
657
|
-
<button class="button is-link is-small" onclick="event.stopPropagation(); showChunkedJson()">
|
|
658
|
-
Switch to Chunked View
|
|
659
|
-
</button>
|
|
660
|
-
</div>
|
|
661
|
-
<div class="control">
|
|
662
|
-
<button class="button is-success is-small" onclick="event.stopPropagation(); showFullJson()">
|
|
663
|
-
Switch to Full View
|
|
664
|
-
</button>
|
|
665
|
-
</div>
|
|
666
|
-
</div>
|
|
667
|
-
</div>
|
|
668
|
-
</div>
|
|
669
|
-
</details>
|
|
670
|
-
`;
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
// Helper functions to update preferences
|
|
674
|
-
function updateAutoChunkPreference(checked) {
|
|
675
|
-
localStorage.setItem('autoChunkLargeJson', checked.toString());
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
function updateThresholdPreference(threshold) {
|
|
679
|
-
localStorage.setItem('jsonSizeThreshold', threshold);
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
async function loadResourceInSidePanel(resourceType, resourceName) {
|
|
683
|
-
document.getElementById('sidePanelTitle').textContent = `${resourceType}: ${resourceName}`;
|
|
684
|
-
openSidePanel();
|
|
685
|
-
showLoading();
|
|
686
|
-
|
|
687
|
-
try {
|
|
688
|
-
// Get current URL parameters to maintain context
|
|
689
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
690
|
-
const region = urlParams.get('region') || '';
|
|
691
|
-
const apiVersion = urlParams.get('api_version') || 'v2';
|
|
692
|
-
|
|
693
|
-
// Build the fetch URL with current parameters
|
|
694
|
-
let fetchUrl = `/ui/resources/${resourceType}/${encodeURIComponent(resourceName)}`;
|
|
695
|
-
const params = new URLSearchParams();
|
|
696
|
-
if (region) params.append('region', region);
|
|
697
|
-
params.append('api_version', apiVersion);
|
|
698
|
-
|
|
699
|
-
if (params.toString()) {
|
|
700
|
-
fetchUrl += '?' + params.toString();
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
const response = await fetch(fetchUrl, {
|
|
704
|
-
credentials: 'same-origin' // Include cookies in the request
|
|
705
|
-
});
|
|
706
|
-
|
|
707
|
-
if (!response.ok) {
|
|
708
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
const data = await response.json();
|
|
712
|
-
const jsonString = JSON.stringify(data, null, 2);
|
|
713
|
-
const sizeKB = (new Blob([jsonString]).size / 1024).toFixed(1);
|
|
714
|
-
const sizeMB = sizeKB / 1024;
|
|
715
|
-
|
|
716
|
-
// Store the data globally
|
|
717
|
-
currentResourceData = {
|
|
718
|
-
data: data,
|
|
719
|
-
jsonString: jsonString,
|
|
720
|
-
sizeKB: sizeKB,
|
|
721
|
-
sizeMB: sizeMB
|
|
722
|
-
};
|
|
723
|
-
|
|
724
|
-
// Check if the JSON is large and show warning/chunked view
|
|
725
|
-
// Use a lower threshold (500KB) to be more conservative about browser performance
|
|
726
|
-
const threshold = localStorage.getItem('jsonSizeThreshold') || 0.5; // MB
|
|
727
|
-
if (sizeMB > threshold) {
|
|
728
|
-
// Check user preference for auto-chunking
|
|
729
|
-
const autoChunk = localStorage.getItem('autoChunkLargeJson') === 'true';
|
|
730
|
-
if (autoChunk) {
|
|
731
|
-
showChunkedJson();
|
|
732
|
-
} else {
|
|
733
|
-
showLargeJsonWarning(resourceType, resourceName);
|
|
734
|
-
}
|
|
735
|
-
} else {
|
|
736
|
-
showFullJson();
|
|
737
|
-
}
|
|
738
|
-
} catch (error) {
|
|
739
|
-
console.error('Error loading resource:', error);
|
|
740
|
-
showError(error.message);
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
function showLargeJsonWarning(resourceType, resourceName) {
|
|
745
|
-
const sizeKB = currentResourceData.sizeKB;
|
|
746
|
-
const sizeMB = currentResourceData.sizeMB;
|
|
747
|
-
|
|
748
|
-
document.getElementById('sidePanelContent').innerHTML = `
|
|
749
|
-
<div class="notification is-warning">
|
|
750
|
-
<p>
|
|
751
|
-
<strong>Large JSON detected (${sizeKB} KB / ${sizeMB.toFixed(1)} MB)</strong><br>
|
|
752
|
-
Loading this much data may slow down your browser. <br>
|
|
753
|
-
</p>
|
|
754
|
-
|
|
755
|
-
Choose an option:
|
|
756
|
-
<button class="button is-dark is-small" onclick="event.stopPropagation(); showChunkedJson()">
|
|
757
|
-
View in Chunks
|
|
758
|
-
</button>
|
|
759
|
-
<button class="button is-dark is-small" onclick="event.stopPropagation(); showFullJson()">
|
|
760
|
-
Load Full JSON
|
|
761
|
-
</button>
|
|
762
|
-
</div>
|
|
763
|
-
`;
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
function showChunkedJson() {
|
|
767
|
-
const lines = currentResourceData.jsonString.split('\n');
|
|
768
|
-
totalChunks = Math.ceil(lines.length / currentChunkSize);
|
|
769
|
-
currentChunkIndex = 0;
|
|
770
|
-
|
|
771
|
-
renderChunkedJson();
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
function showFullJson() {
|
|
775
|
-
// Store the JSON string globally for copying
|
|
776
|
-
window.currentJsonData = currentResourceData.jsonString;
|
|
777
|
-
|
|
778
|
-
document.getElementById('sidePanelContent').innerHTML = `
|
|
779
|
-
<div class="json-container">
|
|
780
|
-
<div class="json-header">
|
|
781
|
-
<span style="color: #d4d4d4; font-size: 0.875rem;">JSON Response (${currentResourceData.sizeKB} KB) - Full View</span>
|
|
782
|
-
<button class="button is-primary is-small" onclick="event.stopPropagation(); copyToClipboard(window.currentJsonData)">
|
|
783
|
-
Copy JSON
|
|
784
|
-
</button>
|
|
785
|
-
</div>
|
|
786
|
-
${getPreferencesHtml()}
|
|
787
|
-
<div class="json-content">${syntaxHighlight(currentResourceData.data)}</div>
|
|
788
|
-
</div>
|
|
789
|
-
`;
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
function renderChunkedJson() {
|
|
793
|
-
const lines = currentResourceData.jsonString.split('\n');
|
|
794
|
-
const startLine = currentChunkIndex * currentChunkSize;
|
|
795
|
-
const endLine = Math.min(startLine + currentChunkSize, lines.length);
|
|
796
|
-
const chunkLines = lines.slice(startLine, endLine);
|
|
797
|
-
const chunkText = chunkLines.join('\n');
|
|
798
|
-
|
|
799
|
-
// Store current chunk for copying
|
|
800
|
-
window.currentJsonData = currentResourceData.jsonString; // Always allow copying full JSON
|
|
801
|
-
window.currentChunkData = chunkText;
|
|
802
|
-
|
|
803
|
-
document.getElementById('sidePanelContent').innerHTML = `
|
|
804
|
-
<div class="json-container">
|
|
805
|
-
<div class="json-header">
|
|
806
|
-
<span style="color: #d4d4d4; font-size: 0.875rem;">JSON Response (${currentResourceData.sizeKB} KB) - Chunked View</span>
|
|
807
|
-
<button class="button is-primary is-small" onclick="event.stopPropagation(); copyToClipboard(window.currentJsonData)">
|
|
808
|
-
Copy Full JSON
|
|
809
|
-
</button>
|
|
810
|
-
</div>
|
|
811
|
-
${getPreferencesHtml()}
|
|
812
|
-
<div class="json-chunk-controls p-4">
|
|
813
|
-
<div class="field is-grouped is-grouped-multiline">
|
|
814
|
-
<div class="control">
|
|
815
|
-
<label class="label is-small has-text-white">Lines per chunk:</label>
|
|
816
|
-
<div class="select is-dark is-small">
|
|
817
|
-
<select onchange="event.stopPropagation(); changeChunkSize(this.value)">
|
|
818
|
-
<option value="50" ${currentChunkSize === 50 ? 'selected' : ''}>50</option>
|
|
819
|
-
<option value="100" ${currentChunkSize === 100 ? 'selected' : ''}>100</option>
|
|
820
|
-
<option value="250" ${currentChunkSize === 250 ? 'selected' : ''}>250</option>
|
|
821
|
-
<option value="500" ${currentChunkSize === 500 ? 'selected' : ''}>500</option>
|
|
822
|
-
<option value="1000" ${currentChunkSize === 1000 ? 'selected' : ''}>1000</option>
|
|
823
|
-
<option value="2000" ${currentChunkSize === 2000 ? 'selected' : ''}>2000</option>
|
|
824
|
-
<option value="5000" ${currentChunkSize === 5000 ? 'selected' : ''}>5000</option>
|
|
825
|
-
</select>
|
|
826
|
-
</div>
|
|
827
|
-
</div>
|
|
828
|
-
<div class="control">
|
|
829
|
-
<div class="buttons">
|
|
830
|
-
<button class="button is-primary is-small" onclick="event.stopPropagation(); previousChunk()" ${currentChunkIndex === 0 ? 'disabled' : ''}>
|
|
831
|
-
← Previous
|
|
832
|
-
</button>
|
|
833
|
-
<button class="button is-primary is-small" onclick="event.stopPropagation(); nextChunk()" ${currentChunkIndex >= totalChunks - 1 ? 'disabled' : ''}>
|
|
834
|
-
Next →
|
|
835
|
-
</button>
|
|
836
|
-
<button class="button is-info is-small" onclick="event.stopPropagation(); copyToClipboard(window.currentChunkData)">
|
|
837
|
-
Copy Chunk
|
|
838
|
-
</button>
|
|
839
|
-
</div>
|
|
840
|
-
</div>
|
|
841
|
-
</div>
|
|
842
|
-
</div>
|
|
843
|
-
<progress class="progress is-primary is-small" value="${endLine}" max="${lines.length}">
|
|
844
|
-
Chunk ${currentChunkIndex + 1} of ${totalChunks}
|
|
845
|
-
</progress>
|
|
846
|
-
<div class="json-content">${syntaxHighlight(chunkText)}</div>
|
|
847
|
-
</div>
|
|
848
|
-
`;
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
function changeChunkSize(newSize) {
|
|
852
|
-
currentChunkSize = parseInt(newSize);
|
|
853
|
-
const lines = currentResourceData.jsonString.split('\n');
|
|
854
|
-
totalChunks = Math.ceil(lines.length / currentChunkSize);
|
|
855
|
-
|
|
856
|
-
// Adjust current chunk index to stay roughly in the same area
|
|
857
|
-
const currentLine = currentChunkIndex * currentChunkSize;
|
|
858
|
-
currentChunkIndex = Math.floor(currentLine / currentChunkSize);
|
|
859
|
-
currentChunkIndex = Math.min(currentChunkIndex, totalChunks - 1);
|
|
860
|
-
|
|
861
|
-
renderChunkedJson();
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
function previousChunk() {
|
|
865
|
-
if (currentChunkIndex > 0) {
|
|
866
|
-
currentChunkIndex--;
|
|
867
|
-
renderChunkedJson();
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
function nextChunk() {
|
|
872
|
-
if (currentChunkIndex < totalChunks - 1) {
|
|
873
|
-
currentChunkIndex++;
|
|
874
|
-
renderChunkedJson();
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
async function loadVirtualHostInSidePanel(routeConfiguration, virtualHost) {
|
|
880
|
-
document.getElementById('sidePanelTitle').textContent = `Virtual Host: ${virtualHost}`;
|
|
881
|
-
openSidePanel();
|
|
882
|
-
showLoading();
|
|
883
|
-
|
|
884
|
-
try {
|
|
885
|
-
// Get current URL parameters to maintain context
|
|
886
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
887
|
-
const region = urlParams.get('region') || '';
|
|
888
|
-
const apiVersion = urlParams.get('api_version') || 'v2';
|
|
889
|
-
|
|
890
|
-
// Build the fetch URL with current parameters
|
|
891
|
-
let fetchUrl = `/ui/resources/routes/${encodeURIComponent(routeConfiguration)}/${encodeURIComponent(virtualHost)}`;
|
|
892
|
-
const params = new URLSearchParams();
|
|
893
|
-
if (region) params.append('region', region);
|
|
894
|
-
params.append('api_version', apiVersion);
|
|
895
|
-
|
|
896
|
-
if (params.toString()) {
|
|
897
|
-
fetchUrl += '?' + params.toString();
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
const response = await fetch(fetchUrl, {
|
|
901
|
-
credentials: 'same-origin' // Include cookies in the request
|
|
902
|
-
});
|
|
903
|
-
|
|
904
|
-
if (!response.ok) {
|
|
905
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
const data = await response.json();
|
|
909
|
-
const jsonString = JSON.stringify(data, null, 2);
|
|
910
|
-
const sizeKB = (new Blob([jsonString]).size / 1024).toFixed(1);
|
|
911
|
-
const sizeMB = sizeKB / 1024;
|
|
912
|
-
|
|
913
|
-
// Store the data globally
|
|
914
|
-
currentResourceData = {
|
|
915
|
-
data: data,
|
|
916
|
-
jsonString: jsonString,
|
|
917
|
-
sizeKB: sizeKB,
|
|
918
|
-
sizeMB: sizeMB
|
|
919
|
-
};
|
|
920
|
-
|
|
921
|
-
// Check if the JSON is large and show warning/chunked view
|
|
922
|
-
const threshold = localStorage.getItem('jsonSizeThreshold') || 0.5; // MB
|
|
923
|
-
if (sizeMB > threshold) {
|
|
924
|
-
// Check user preference for auto-chunking
|
|
925
|
-
const autoChunk = localStorage.getItem('autoChunkLargeJson') === 'true';
|
|
926
|
-
if (autoChunk) {
|
|
927
|
-
showChunkedJson();
|
|
928
|
-
} else {
|
|
929
|
-
showLargeJsonWarning('Virtual Host', virtualHost);
|
|
930
|
-
}
|
|
931
|
-
} else {
|
|
932
|
-
showFullJson();
|
|
933
|
-
}
|
|
934
|
-
} catch (error) {
|
|
935
|
-
console.error('Error loading virtual host:', error);
|
|
936
|
-
showError(error.message);
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
// Close side panel when clicking outside of it
|
|
941
|
-
document.addEventListener('click', function(event) {
|
|
942
|
-
const sidePanel = document.getElementById('sidePanel');
|
|
943
|
-
const backdrop = document.getElementById('sidePanelBackdrop');
|
|
944
|
-
const isClickInsidePanel = sidePanel.contains(event.target);
|
|
945
|
-
const isResourceLink = event.target.closest('.panel-block');
|
|
946
|
-
const isBackdropClick = event.target === backdrop;
|
|
947
|
-
|
|
948
|
-
if ((isBackdropClick || (!isClickInsidePanel && !isResourceLink)) && sidePanel.classList.contains('is-active')) {
|
|
949
|
-
closeSidePanel();
|
|
950
|
-
}
|
|
951
|
-
});
|
|
952
|
-
|
|
953
|
-
// Keyboard shortcuts
|
|
954
|
-
document.addEventListener('keydown', function(event) {
|
|
955
|
-
if (event.key === 'Escape') {
|
|
956
|
-
const sidePanel = document.getElementById('sidePanel');
|
|
957
|
-
if (sidePanel.classList.contains('is-active')) {
|
|
958
|
-
closeSidePanel();
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
});
|
|
962
|
-
|
|
963
|
-
const perPage = 10;
|
|
964
|
-
let currentPage = 1;
|
|
965
|
-
|
|
966
|
-
function filter_results(id) {
|
|
967
|
-
const input = document.getElementById(id);
|
|
968
|
-
const filter = input.value.toLowerCase();
|
|
969
|
-
|
|
970
|
-
filteredResources = resources.filter((res) => {
|
|
971
|
-
const name = res.name || "";
|
|
972
|
-
return name.toLowerCase().includes(filter);
|
|
973
|
-
});
|
|
974
|
-
|
|
975
|
-
currentPage = 1;
|
|
976
|
-
renderPage(currentPage);
|
|
977
|
-
|
|
978
|
-
// Update the resource count display
|
|
979
|
-
const countElement = document.getElementById('resource-count');
|
|
980
|
-
if (countElement) {
|
|
981
|
-
const count = filteredResources.length;
|
|
982
|
-
const plural = count === 1 ? 'resource' : 'resources';
|
|
983
|
-
countElement.textContent = `${count} ${plural}`;
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
function renderPage(page) {
|
|
988
|
-
const container = document.getElementById('resource-container');
|
|
989
|
-
container.innerHTML = '';
|
|
990
|
-
|
|
991
|
-
const start = (page - 1) * perPage;
|
|
992
|
-
const end = start + perPage;
|
|
993
|
-
const pageItems = filteredResources.slice(start, end);
|
|
994
|
-
|
|
995
|
-
for (const resource of pageItems) {
|
|
996
|
-
const name = resource.name;
|
|
997
|
-
const item = document.createElement('a');
|
|
998
|
-
item.className = 'panel-block has-text-weight-medium';
|
|
999
|
-
item.href = '#';
|
|
1000
|
-
item.onclick = (e) => {
|
|
1001
|
-
e.preventDefault();
|
|
1002
|
-
loadResourceInSidePanel('{{ resource_type }}', name);
|
|
1003
|
-
};
|
|
1004
|
-
item.innerHTML = `
|
|
1005
|
-
<span class="panel-icon">
|
|
1006
|
-
<i class="fas fa-arrow-right" aria-hidden="true"></i>
|
|
1007
|
-
</span>
|
|
1008
|
-
${name}
|
|
1009
|
-
`;
|
|
1010
|
-
container.appendChild(item);
|
|
1011
|
-
}
|
|
1012
|
-
renderPaginationControls();
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
function renderPaginationControls() {
|
|
1016
|
-
const totalPages = Math.ceil(filteredResources.length / perPage);
|
|
1017
|
-
const pageList = document.getElementById('page-numbers');
|
|
1018
|
-
pageList.innerHTML = '';
|
|
1019
|
-
|
|
1020
|
-
for (let i = 1; i <= totalPages; i++) {
|
|
1021
|
-
const li = document.createElement('li');
|
|
1022
|
-
const a = document.createElement('a');
|
|
1023
|
-
a.className = 'pagination-link';
|
|
1024
|
-
if (i === currentPage) a.classList.add('is-current');
|
|
1025
|
-
a.textContent = i;
|
|
1026
|
-
a.addEventListener('click', () => {
|
|
1027
|
-
currentPage = i;
|
|
1028
|
-
renderPage(currentPage);
|
|
1029
|
-
});
|
|
1030
|
-
li.appendChild(a);
|
|
1031
|
-
pageList.appendChild(li);
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
document.getElementById('prev-btn').disabled = currentPage === 1;
|
|
1035
|
-
document.getElementById('next-btn').disabled = currentPage === totalPages;
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
document.getElementById('prev-btn').addEventListener('click', () => {
|
|
1039
|
-
if (currentPage > 1) {
|
|
1040
|
-
currentPage--;
|
|
1041
|
-
renderPage(currentPage);
|
|
1042
|
-
}
|
|
1043
|
-
});
|
|
1044
|
-
|
|
1045
|
-
document.getElementById('next-btn').addEventListener('click', () => {
|
|
1046
|
-
const totalPages = Math.ceil(filteredResources.length / perPage);
|
|
1047
|
-
if (currentPage < totalPages) {
|
|
1048
|
-
currentPage++;
|
|
1049
|
-
renderPage(currentPage);
|
|
1050
|
-
}
|
|
1051
|
-
});
|
|
1052
|
-
renderPage(currentPage);
|
|
244
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
245
|
+
const resourceNames = [
|
|
246
|
+
{% for resource in resources %}
|
|
247
|
+
"{{ resource.name or resource.cluster_name or 'Unknown' }}"{% if not loop.last %},{% endif %}
|
|
248
|
+
{% endfor %}
|
|
249
|
+
];
|
|
250
|
+
if (typeof initializeResources === 'function') {
|
|
251
|
+
initializeResources(resourceNames, '{{ resource_type }}');
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
function setEnvoyVersion(version) {
|
|
255
|
+
document.cookie = `envoy_version=${version}; path=/ui/resources/; max-age=31536000`;
|
|
256
|
+
window.location.reload();
|
|
257
|
+
}
|
|
1053
258
|
</script>
|
|
1054
259
|
</div>
|
|
1055
260
|
{% endblock %}
|