pywebexec 2.3.13__py3-none-any.whl → 2.4.0__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.
- pywebexec/pywebexec.py +24 -21
- pywebexec/static/css/form.css +24 -19
- pywebexec/static/css/style.css +70 -3
- pywebexec/static/js/script.js +19 -5
- pywebexec/static/js/tablefilter.js +149 -0
- pywebexec/templates/index.html +3 -2
- pywebexec/version.py +2 -2
- {pywebexec-2.3.13.dist-info → pywebexec-2.4.0.dist-info}/METADATA +1 -1
- {pywebexec-2.3.13.dist-info → pywebexec-2.4.0.dist-info}/RECORD +13 -12
- {pywebexec-2.3.13.dist-info → pywebexec-2.4.0.dist-info}/WHEEL +0 -0
- {pywebexec-2.3.13.dist-info → pywebexec-2.4.0.dist-info}/entry_points.txt +0 -0
- {pywebexec-2.3.13.dist-info → pywebexec-2.4.0.dist-info}/licenses/LICENSE +0 -0
- {pywebexec-2.3.13.dist-info → pywebexec-2.4.0.dist-info}/top_level.txt +0 -0
pywebexec/pywebexec.py
CHANGED
@@ -22,7 +22,7 @@ if platform.system() != 'Windows':
|
|
22
22
|
import termios
|
23
23
|
else:
|
24
24
|
from waitress import serve
|
25
|
-
from winpty import
|
25
|
+
from winpty import PTY, WinptyError
|
26
26
|
import ipaddress
|
27
27
|
from socket import socket, AF_INET, SOCK_STREAM
|
28
28
|
import ssl
|
@@ -513,7 +513,6 @@ def script(output_file):
|
|
513
513
|
signal.signal(signal.SIGWINCH, sigwinch_passthrough)
|
514
514
|
p.interact()
|
515
515
|
|
516
|
-
|
517
516
|
def run_command(fromip, user, command, params, command_id, rows, cols):
|
518
517
|
log_info(fromip, user, f'run_command {command_id}: {command_str(command, params)}')
|
519
518
|
start_time = datetime.now(timezone.utc).isoformat()
|
@@ -545,21 +544,25 @@ def run_command(fromip, user, command, params, command_id, rows, cols):
|
|
545
544
|
elif platform.system() == 'Windows':
|
546
545
|
# On Windows, use winpty
|
547
546
|
with open(output_file_path, 'wb', buffering=0) as fd:
|
548
|
-
p =
|
549
|
-
|
547
|
+
p = PTY(cols, rows)
|
548
|
+
p.spawn(subprocess.list2cmdline([sys.executable, "-u", command, *params]))
|
550
549
|
update_command_status(command_id, {
|
551
|
-
'pid': pid,
|
550
|
+
'pid': p.pid,
|
552
551
|
})
|
553
552
|
while True:
|
554
553
|
try:
|
555
|
-
|
556
|
-
|
554
|
+
# sleep less than 0.1s to get full output after command ends
|
555
|
+
# pty won't be readable 0.1s after command ends
|
556
|
+
time.sleep(0.09)
|
557
|
+
# cannot use blocking read as it will block forever if no output at end of commandgit d
|
558
|
+
data = p.read(10485760, blocking=False)
|
559
|
+
fd.write(data.encode())
|
560
|
+
if not p.isalive():
|
557
561
|
break
|
558
|
-
fd.write(data)
|
559
562
|
except (EOFError, WinptyError):
|
560
563
|
break
|
561
|
-
status = p.
|
562
|
-
p
|
564
|
+
status = p.get_exitstatus()
|
565
|
+
del p
|
563
566
|
else:
|
564
567
|
# On Unix, use pexpect
|
565
568
|
with open(output_file_path, 'wb') as fd:
|
@@ -575,29 +578,29 @@ def run_command(fromip, user, command, params, command_id, rows, cols):
|
|
575
578
|
end_time = datetime.now(timezone.utc).isoformat()
|
576
579
|
# Update the status based on the result
|
577
580
|
if status is None:
|
578
|
-
|
581
|
+
status = -15
|
582
|
+
if status in [15, -15] :
|
579
583
|
update_command_status(command_id, {
|
580
584
|
'status': 'aborted',
|
581
585
|
'end_time': end_time,
|
582
|
-
'exit_code':
|
586
|
+
'exit_code': status,
|
583
587
|
})
|
584
588
|
log_info(fromip, user, f'run_command {command_id}: {command_str(command, params)}: command aborted')
|
585
589
|
else:
|
586
|
-
|
587
|
-
if exit_code == 0:
|
590
|
+
if status == 0:
|
588
591
|
update_command_status(command_id, {
|
589
592
|
'status': 'success',
|
590
593
|
'end_time': end_time,
|
591
|
-
'exit_code':
|
594
|
+
'exit_code': status,
|
592
595
|
})
|
593
596
|
log_info(fromip, user, f'run_command {command_id}: {command_str(command, params)}: completed successfully')
|
594
597
|
else:
|
595
598
|
update_command_status(command_id, {
|
596
599
|
'status': 'failed',
|
597
600
|
'end_time': end_time,
|
598
|
-
'exit_code':
|
601
|
+
'exit_code': status,
|
599
602
|
})
|
600
|
-
log_info(fromip, user, f'run_command {command_id}: {command_str(command, params)}: exit code {
|
603
|
+
log_info(fromip, user, f'run_command {command_id}: {command_str(command, params)}: exit code {status}')
|
601
604
|
|
602
605
|
except Exception as e:
|
603
606
|
end_time = datetime.now(timezone.utc).isoformat()
|
@@ -609,8 +612,8 @@ def run_command(fromip, user, command, params, command_id, rows, cols):
|
|
609
612
|
with open(get_output_file_path(command_id), 'a') as output_file:
|
610
613
|
output_file.write(str(e))
|
611
614
|
app.logger.error(fromip, user, f'Error running command {command_id}: {e}')
|
612
|
-
|
613
|
-
return
|
615
|
+
status = 1
|
616
|
+
return status
|
614
617
|
|
615
618
|
def command_str(command, params):
|
616
619
|
try:
|
@@ -735,9 +738,9 @@ def stop_command(command_id):
|
|
735
738
|
end_time = datetime.now(timezone.utc).isoformat()
|
736
739
|
try:
|
737
740
|
try:
|
738
|
-
os.killpg(os.getpgid(pid), signal.SIGTERM)
|
741
|
+
os.killpg(os.getpgid(pid), signal.SIGTERM)
|
739
742
|
except:
|
740
|
-
os.kill(pid, signal.
|
743
|
+
os.kill(pid, signal.SIGTERM)
|
741
744
|
except Exception as e:
|
742
745
|
update_command_status(command_id, {
|
743
746
|
'status': 'aborted',
|
pywebexec/static/css/form.css
CHANGED
@@ -4,6 +4,27 @@
|
|
4
4
|
src: url('../fonts/glyphicons-halflings-regular.eot');
|
5
5
|
src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
|
6
6
|
}
|
7
|
+
.glyphicon-plus-sign:before {
|
8
|
+
content: "\e081";
|
9
|
+
}
|
10
|
+
.glyphicon-minus-sign:before {
|
11
|
+
content: "\e082";
|
12
|
+
}
|
13
|
+
.glyphicon-search:before {
|
14
|
+
content: "\e003";
|
15
|
+
}
|
16
|
+
.glyphicon {
|
17
|
+
position: relative;
|
18
|
+
top: 1px;
|
19
|
+
display: inline-block;
|
20
|
+
font-family: 'Glyphicons Halflings';
|
21
|
+
font-style: normal;
|
22
|
+
font-weight: normal;
|
23
|
+
line-height: 1;
|
24
|
+
-webkit-font-smoothing: antialiased;
|
25
|
+
-moz-osx-font-smoothing: grayscale;
|
26
|
+
}
|
27
|
+
|
7
28
|
|
8
29
|
#schemaForm, .schema-form {
|
9
30
|
font-size: 14px;
|
@@ -12,24 +33,6 @@
|
|
12
33
|
div {
|
13
34
|
scrollbar-width: thin;
|
14
35
|
}
|
15
|
-
.glyphicon-plus-sign:before {
|
16
|
-
content: "\e081";
|
17
|
-
}
|
18
|
-
.glyphicon-minus-sign:before {
|
19
|
-
content: "\e082";
|
20
|
-
}
|
21
|
-
|
22
|
-
.glyphicon {
|
23
|
-
position: relative;
|
24
|
-
top: 1px;
|
25
|
-
display: inline-block;
|
26
|
-
font-family: 'Glyphicons Halflings';
|
27
|
-
font-style: normal;
|
28
|
-
font-weight: normal;
|
29
|
-
line-height: 1;
|
30
|
-
-webkit-font-smoothing: antialiased;
|
31
|
-
-moz-osx-font-smoothing: grayscale;
|
32
|
-
}
|
33
36
|
|
34
37
|
.btn {
|
35
38
|
font-size: 14px;
|
@@ -71,6 +74,7 @@
|
|
71
74
|
display: block;
|
72
75
|
margin-top: 5px;
|
73
76
|
margin-bottom: 10px;
|
77
|
+
max-width: 300px;
|
74
78
|
}
|
75
79
|
.form-control {
|
76
80
|
font-family: Arial, Helvetica, sans-serif;
|
@@ -317,4 +321,5 @@
|
|
317
321
|
.swagger-ui textarea {
|
318
322
|
min-height: 130px;
|
319
323
|
overflow-y: hidden;
|
320
|
-
}
|
324
|
+
}
|
325
|
+
|
pywebexec/static/css/style.css
CHANGED
@@ -46,6 +46,57 @@ th {
|
|
46
46
|
position: sticky;
|
47
47
|
top: 0;
|
48
48
|
z-index: 1;
|
49
|
+
vertical-align: top;
|
50
|
+
}
|
51
|
+
.th-content {
|
52
|
+
display: flex;
|
53
|
+
align-items: center;
|
54
|
+
gap: 5px;
|
55
|
+
white-space: nowrap;
|
56
|
+
}
|
57
|
+
.sort-btn {
|
58
|
+
color: #aaa;
|
59
|
+
font-size: 14px;
|
60
|
+
user-select: none;
|
61
|
+
display: inline-block;
|
62
|
+
width: 7px;
|
63
|
+
text-align: center;
|
64
|
+
flex-shrink: 0;
|
65
|
+
}
|
66
|
+
.sort-btn[data-sort-order="asc"] {
|
67
|
+
color: #5a5;
|
68
|
+
}
|
69
|
+
.sort-btn[data-sort-order="desc"] {
|
70
|
+
color: #5a5;
|
71
|
+
}
|
72
|
+
.column-filter {
|
73
|
+
display: block;
|
74
|
+
width: 90%;
|
75
|
+
max-width: 300px;
|
76
|
+
margin: 5px 0px 0px 0px;
|
77
|
+
padding: 2px 5px;
|
78
|
+
text-indent: 5px;
|
79
|
+
border: 1px solid #666;
|
80
|
+
border-radius: 15px;
|
81
|
+
font-size: 12px;
|
82
|
+
background: #444;
|
83
|
+
color: #eee
|
84
|
+
}
|
85
|
+
.column-filter:focus {
|
86
|
+
outline: none;
|
87
|
+
border-color: #666;
|
88
|
+
background: #ddd;
|
89
|
+
color: #222;
|
90
|
+
}
|
91
|
+
.column-filter:focus::placeholder {
|
92
|
+
color: transparent;
|
93
|
+
}
|
94
|
+
.column-filter::placeholder {
|
95
|
+
font-family: 'Glyphicons Halflings';
|
96
|
+
/* content: "\e003"; */
|
97
|
+
font-size: 12px;
|
98
|
+
/* text-align: right; */
|
99
|
+
color: #888;
|
49
100
|
}
|
50
101
|
.outcol {
|
51
102
|
width: 100%;
|
@@ -133,6 +184,7 @@ form {
|
|
133
184
|
width: 16px;
|
134
185
|
height: 16px;
|
135
186
|
min-width: 16px;
|
187
|
+
margin-left: 5px;
|
136
188
|
margin-right: 5px;
|
137
189
|
background-size: contain;
|
138
190
|
background-repeat: no-repeat;
|
@@ -254,7 +306,7 @@ code {
|
|
254
306
|
font-size: 13px;
|
255
307
|
}
|
256
308
|
|
257
|
-
.show-command-list-button .
|
309
|
+
.show-command-list-button .ar- {
|
258
310
|
visibility: hidden; /* Hide arrow by default */
|
259
311
|
}
|
260
312
|
|
@@ -469,13 +521,24 @@ body.dimmed * {
|
|
469
521
|
}
|
470
522
|
.nbrunning {
|
471
523
|
display: inline-block;
|
472
|
-
font-size:
|
524
|
+
font-size: 10px;
|
473
525
|
font-weight: normal;
|
474
526
|
border: 1px solid #aaa;
|
475
527
|
border-radius: 17px;
|
476
528
|
min-width: 17px;
|
477
529
|
text-align: center;
|
478
530
|
}
|
531
|
+
.row-count {
|
532
|
+
display: inline-block;
|
533
|
+
font-size: 11px;
|
534
|
+
font-weight: normal;
|
535
|
+
border: 1px solid #aaa;
|
536
|
+
border-radius: 17px;
|
537
|
+
/* min-width: 17px; */
|
538
|
+
text-align: center;
|
539
|
+
padding: 0px 4px;
|
540
|
+
margin-left: auto;
|
541
|
+
}
|
479
542
|
|
480
543
|
.xterm-accessibility {
|
481
544
|
display: none;
|
@@ -483,4 +546,8 @@ body.dimmed * {
|
|
483
546
|
.swaggerlink {
|
484
547
|
text-decoration: none;
|
485
548
|
font-size: 10px;
|
486
|
-
}
|
549
|
+
}
|
550
|
+
|
551
|
+
#statusRunning > span {
|
552
|
+
vertical-align: text-top;
|
553
|
+
}
|
pywebexec/static/js/script.js
CHANGED
@@ -181,14 +181,19 @@ async function fetchCommands(hide=false) {
|
|
181
181
|
</td>
|
182
182
|
`;
|
183
183
|
commandsTbody.appendChild(commandRow);
|
184
|
+
const commandsTable = document.getElementById('commandsTable');
|
184
185
|
});
|
185
186
|
if (runningCommands.length) {
|
186
|
-
document.getElementById('
|
187
|
-
|
187
|
+
const thStatus = document.getElementById('statusRunning');
|
188
|
+
thStatus.innerHTML = `Running <span class="status-icon status-running"></span><span class="system-font nbrunning">${runningCommands.length}</span>`;
|
189
|
+
thStatus.setAttribute('title', runningCommands.join("\n"));
|
188
190
|
} else {
|
189
|
-
document.getElementById('
|
190
|
-
|
191
|
+
const thStatus = document.getElementById('statusRunning');
|
192
|
+
thStatus.innerHTML = `Status <span class="status-icon status-norun"></span>`;
|
193
|
+
thStatus.setAttribute('title', "no command running");
|
191
194
|
}
|
195
|
+
// Apply filters after table update
|
196
|
+
applyFilters(document.getElementById('commandsTable'));
|
192
197
|
document.getElementById('dimmer').style.display = 'none';
|
193
198
|
} catch (error) {
|
194
199
|
console.log('Error fetching commands:', error);
|
@@ -227,6 +232,10 @@ async function fetchOutput(url) {
|
|
227
232
|
if (htmlContent) {
|
228
233
|
document.getElementById('output').innerHTML = htmlContent;
|
229
234
|
document.getElementById('output').classList.add('outputhtml');
|
235
|
+
const table = document.getElementById('output').querySelector('table');
|
236
|
+
if (table != undefined && table != null) {
|
237
|
+
initTableFilters(table);
|
238
|
+
}
|
230
239
|
} else {
|
231
240
|
if (slider.value == 1000)
|
232
241
|
terminal.write(data.output);
|
@@ -321,6 +330,11 @@ async function viewOutput(command_id) {
|
|
321
330
|
if (htmlContent) {
|
322
331
|
document.getElementById('output').innerHTML = htmlContent;
|
323
332
|
document.getElementById('output').classList.add('outputhtml');
|
333
|
+
const table = document.getElementById('output').querySelector('table');
|
334
|
+
console.log(table);
|
335
|
+
if (table != undefined && table != null) {
|
336
|
+
initTableFilters(table);
|
337
|
+
}
|
324
338
|
} else {
|
325
339
|
terminal.write(output);
|
326
340
|
}
|
@@ -535,7 +549,7 @@ toggleButton.addEventListener('click', toggleFetchOutput);
|
|
535
549
|
toggleFitButton.addEventListener('click', toggleFit);
|
536
550
|
setFitIcon();
|
537
551
|
|
538
|
-
document.getElementById('
|
552
|
+
document.getElementById('statusRunning').addEventListener('click', () => {
|
539
553
|
showRunningOnly = !showRunningOnly;
|
540
554
|
hiddenCommandIds = [];
|
541
555
|
fetchCommands(showRunningOnly);
|
@@ -0,0 +1,149 @@
|
|
1
|
+
function initTableFilters(table) {
|
2
|
+
const headers = table.querySelectorAll('thead th');
|
3
|
+
headers.forEach((header, index) => {
|
4
|
+
if (index !== 4 || table!==commandsTable) { // Skip Action column
|
5
|
+
const contentSpan = document.createElement('span');
|
6
|
+
contentSpan.className = 'th-content';
|
7
|
+
|
8
|
+
// Add sort button first
|
9
|
+
const sortBtn = document.createElement('span');
|
10
|
+
sortBtn.className = 'sort-btn';
|
11
|
+
sortBtn.innerHTML = '⇕';
|
12
|
+
sortBtn.style.cursor = 'pointer';
|
13
|
+
sortBtn.setAttribute('data-sort-order', '');
|
14
|
+
sortBtn.onclick = () => toggleSort(table, index, sortBtn);
|
15
|
+
|
16
|
+
// Move existing elements into the content span
|
17
|
+
while (header.firstChild) {
|
18
|
+
contentSpan.appendChild(header.firstChild);
|
19
|
+
}
|
20
|
+
|
21
|
+
// Add sort button at the beginning
|
22
|
+
contentSpan.insertBefore(sortBtn, contentSpan.firstChild);
|
23
|
+
|
24
|
+
// Add row counter for last column
|
25
|
+
if (index === headers.length - 1) {
|
26
|
+
const rowCount = document.createElement('span');
|
27
|
+
rowCount.className = 'row-count';
|
28
|
+
rowCount.classList.add('system-font');
|
29
|
+
contentSpan.appendChild(rowCount);
|
30
|
+
}
|
31
|
+
|
32
|
+
header.appendChild(contentSpan);
|
33
|
+
|
34
|
+
// Add filter input
|
35
|
+
const input = document.createElement('input');
|
36
|
+
input.type = 'search';
|
37
|
+
input.className = 'column-filter';
|
38
|
+
input.placeholder = ''; // Unicode for magnifying glass
|
39
|
+
input.addEventListener('input', () => applyFilters(table));
|
40
|
+
header.appendChild(input);
|
41
|
+
}
|
42
|
+
});
|
43
|
+
// Initialize row count
|
44
|
+
updateRowCount(table, table.querySelectorAll('tbody tr').length);
|
45
|
+
}
|
46
|
+
|
47
|
+
function updateRowCount(table, count) {
|
48
|
+
const rowCount = table.querySelector('.row-count');
|
49
|
+
if (rowCount) {
|
50
|
+
rowCount.textContent = `${count}`;
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
function toggleSort(table, colIndex, sortBtn) {
|
55
|
+
// Reset other sort buttons
|
56
|
+
table.querySelectorAll('.sort-btn').forEach(btn => {
|
57
|
+
if (btn !== sortBtn) {
|
58
|
+
btn.setAttribute('data-sort-order', '');
|
59
|
+
btn.innerHTML = '⇕';
|
60
|
+
}
|
61
|
+
});
|
62
|
+
|
63
|
+
// Toggle sort order
|
64
|
+
const currentOrder = sortBtn.getAttribute('data-sort-order');
|
65
|
+
let newOrder = 'asc';
|
66
|
+
if (currentOrder === 'asc') {
|
67
|
+
newOrder = 'desc';
|
68
|
+
sortBtn.innerHTML = '⇓';
|
69
|
+
} else if (currentOrder === 'desc') {
|
70
|
+
newOrder = '';
|
71
|
+
sortBtn.innerHTML = '⇕';
|
72
|
+
} else {
|
73
|
+
sortBtn.innerHTML = '⇑';
|
74
|
+
}
|
75
|
+
sortBtn.setAttribute('data-sort-order', newOrder);
|
76
|
+
sortBtn.setAttribute('data-col-index', colIndex); // Store column index on the button
|
77
|
+
applyFilters(table);
|
78
|
+
}
|
79
|
+
|
80
|
+
function applyFilters(table) {
|
81
|
+
const rows = Array.from(table.querySelectorAll('tbody tr'));
|
82
|
+
const filters = Array.from(table.querySelectorAll('.column-filter'))
|
83
|
+
.map(filter => ({
|
84
|
+
value: filter.value.toLowerCase(),
|
85
|
+
index: filter.parentElement.cellIndex,
|
86
|
+
regexp: filter.value ? (() => {
|
87
|
+
try { return new RegExp(filter.value, 'i'); }
|
88
|
+
catch(e) { return null; }
|
89
|
+
})() : null
|
90
|
+
}));
|
91
|
+
|
92
|
+
// First apply filters
|
93
|
+
const filteredRows = rows.filter(row => {
|
94
|
+
// If no filters are active, show all rows
|
95
|
+
if (filters.every(f => !f.value)) {
|
96
|
+
row.style.display = '';
|
97
|
+
return true;
|
98
|
+
}
|
99
|
+
const cells = row.cells;
|
100
|
+
const shouldShow = !filters.some(filter => {
|
101
|
+
if (!filter.value) return false;
|
102
|
+
const cellText = cells[filter.index]?.innerText || '';
|
103
|
+
if (filter.regexp) return !filter.regexp.test(cellText);
|
104
|
+
return !cellText.toLowerCase().includes(filter.value);
|
105
|
+
});
|
106
|
+
row.style.display = shouldShow ? '' : 'none';
|
107
|
+
return shouldShow;
|
108
|
+
});
|
109
|
+
|
110
|
+
// Update row count
|
111
|
+
updateRowCount(table, filteredRows.length);
|
112
|
+
|
113
|
+
// Then apply sorting if active
|
114
|
+
const sortBtn = table.querySelector('.sort-btn[data-sort-order]:not([data-sort-order=""])');
|
115
|
+
if (sortBtn) {
|
116
|
+
const colIndex = parseInt(sortBtn.getAttribute('data-col-index'));
|
117
|
+
const sortOrder = sortBtn.getAttribute('data-sort-order');
|
118
|
+
|
119
|
+
filteredRows.sort((a, b) => {
|
120
|
+
const aVal = a.cells[colIndex]?.innerText.trim() || '';
|
121
|
+
const bVal = b.cells[colIndex]?.innerText.trim() || '';
|
122
|
+
|
123
|
+
// Check if both values are numeric
|
124
|
+
const aNum = !isNaN(aVal) && !isNaN(parseFloat(aVal));
|
125
|
+
const bNum = !isNaN(bVal) && !isNaN(parseFloat(bVal));
|
126
|
+
|
127
|
+
if (aNum && bNum) {
|
128
|
+
// Numeric comparison
|
129
|
+
return sortOrder === 'asc'
|
130
|
+
? parseFloat(aVal) - parseFloat(bVal)
|
131
|
+
: parseFloat(bVal) - parseFloat(aVal);
|
132
|
+
}
|
133
|
+
|
134
|
+
// Fallback to string comparison
|
135
|
+
if (aVal < bVal) return sortOrder === 'asc' ? -1 : 1;
|
136
|
+
if (aVal > bVal) return sortOrder === 'asc' ? 1 : -1;
|
137
|
+
return 0;
|
138
|
+
});
|
139
|
+
|
140
|
+
// Reorder visible rows
|
141
|
+
const tbody = table.querySelector('tbody');
|
142
|
+
filteredRows.forEach(row => tbody.appendChild(row));
|
143
|
+
}
|
144
|
+
}
|
145
|
+
|
146
|
+
let commandsTable = document.querySelector('#commandsTable');
|
147
|
+
document.addEventListener('DOMContentLoaded', () => {
|
148
|
+
if (commandsTable) initTableFilters(commandsTable);
|
149
|
+
});
|
pywebexec/templates/index.html
CHANGED
@@ -33,13 +33,13 @@
|
|
33
33
|
<div id="paramsHelp" class="markdown"></div>
|
34
34
|
</div>
|
35
35
|
<div class="table-container" id="tableContainer">
|
36
|
-
<table>
|
36
|
+
<table id="commandsTable">
|
37
37
|
<thead>
|
38
38
|
<tr>
|
39
39
|
<th>ID</th>
|
40
40
|
<th>Start Time</th>
|
41
41
|
<th>Duration</th>
|
42
|
-
<th id="thStatus"><
|
42
|
+
<th id="thStatus"><div id="statusRunning"></div></th>
|
43
43
|
<th>Action</th>
|
44
44
|
<th>Command</th>
|
45
45
|
<th>Output</th>
|
@@ -75,6 +75,7 @@
|
|
75
75
|
<script type="text/javascript" src="/static/js/script.js"></script>
|
76
76
|
<script type="text/javascript" src="/static/js/executables.js"></script>
|
77
77
|
<script type="text/javascript" src="/static/js/schemaform.js"></script>
|
78
|
+
<script type="text/javascript" src="/static/js/tablefilter.js"></script>
|
78
79
|
|
79
80
|
|
80
81
|
</body>
|
pywebexec/version.py
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
pywebexec/__init__.py,sha256=197fHJy0UDBwTTpGCGortZRr-w2kTaD7MxqdbVmTEi0,61
|
2
2
|
pywebexec/host_ip.py,sha256=oiCMlo2o3AkkgXDarUSx8T3FWXKI0vk1-EPnx5FGBd8,1332
|
3
|
-
pywebexec/pywebexec.py,sha256=
|
3
|
+
pywebexec/pywebexec.py,sha256=NT4f7Xd4qMkAgjgwguqKKbUkOTCqT7ArRYlsW57Pfwg,48477
|
4
4
|
pywebexec/swagger.yaml,sha256=I_oLpp7Hqel8SDEEykvpmCT-Gv3ytGlziq9bvQOrtZY,7598
|
5
|
-
pywebexec/version.py,sha256=
|
6
|
-
pywebexec/static/css/form.css,sha256=
|
5
|
+
pywebexec/version.py,sha256=KyHVhKcn4Ob8_JP09Buz8x5uB4b7RJZLl3FBlZ8sWSY,511
|
6
|
+
pywebexec/static/css/form.css,sha256=2JUhraeL46JaiNoD_MSKA2JdouHkXaamhd77DnCqG8k,7291
|
7
7
|
pywebexec/static/css/markdown.css,sha256=br4-iK9wigTs54N2KHtjgZ4KLH0THVSvJo-XZAdMHiE,1970
|
8
|
-
pywebexec/static/css/style.css,sha256=
|
8
|
+
pywebexec/static/css/style.css,sha256=mdLDqKIGLxJtR9tSaThL8po0CXoEkm9P0k_gS8_wPcA,11652
|
9
9
|
pywebexec/static/css/swagger-ui.css,sha256=xhXN8fnUaIACGHuPIEIr9-qmyYr6Zx0k2wv4Qy7Bg1Y,154985
|
10
10
|
pywebexec/static/css/swagger-ui.css.map,sha256=dJy-xBn_htK4BNupTMIl33ddse7BXsrCdDJWlTJodnw,258842
|
11
11
|
pywebexec/static/css/xterm.css,sha256=uo5phWaUiJgcz0DAzv46uoByLLbJLeetYosL1xf68rY,5559
|
@@ -36,8 +36,9 @@ pywebexec/static/images/swagger-ui.svg,sha256=FR0yeOVwe4zCYKZAjCGcT_m0Mf25NexIVa
|
|
36
36
|
pywebexec/static/js/executables.js,sha256=cTgCFHr_F9bFCirtfG_uR32vOY3vNUr4Ih3Wglj5lFc,11988
|
37
37
|
pywebexec/static/js/popup.js,sha256=IaKmk2U2hEn-Nv6krf_PPW6LaG8NcpCkJKb7lUX0qZo,11457
|
38
38
|
pywebexec/static/js/schemaform.js,sha256=2AIjwdjSDTE2ide8UMmQt4tS-7-JKqidKdopq9mNzvo,12458
|
39
|
-
pywebexec/static/js/script.js,sha256=
|
39
|
+
pywebexec/static/js/script.js,sha256=SpNmskHKJHza0Au7QWrb17EKqiMPbMz5CDmaLt_i3M4,21548
|
40
40
|
pywebexec/static/js/swagger-form.js,sha256=CLcSHMhk5P4-_2MIRBoJLgEnIj_9keDDSzUugXHZjio,4565
|
41
|
+
pywebexec/static/js/tablefilter.js,sha256=ml6oGCik11W61ifGWqLLmY_sC7gOH1tsqOFXFQ336lo,5590
|
41
42
|
pywebexec/static/js/js-yaml/LICENSE,sha256=oHvCRGi5ZUznalR9R6LbKC0HcztxXbTHOpi9Y5YflVA,1084
|
42
43
|
pywebexec/static/js/js-yaml/js-yaml.min.js,sha256=Rdw90D3AegZwWiwpibjH9wkBPwS9U4bjJ51ORH8H69c,39430
|
43
44
|
pywebexec/static/js/marked/LICENSE.md,sha256=jjo_gvWaYJWPVsoI9EVkfDKkcz3HymwsRvbriYRxq5w,2942
|
@@ -64,12 +65,12 @@ pywebexec/static/jsonform/deps/underscore.js,sha256=SzKOQsVYGX1bmddyfPzGC6yXY_rW
|
|
64
65
|
pywebexec/static/jsonform/deps/img/glyphicons-halflings.png,sha256=hpJM0AbbMLnU8UGOBs172D7vK-dooQ8n0s_ybml3zO0,13826
|
65
66
|
pywebexec/static/jsonform/lib/jsonform.js,sha256=U-BvOgq5gCvSUo36qSAK7Y91RPKOq7vZShkIYpzwlkk,138525
|
66
67
|
pywebexec/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
67
|
-
pywebexec/templates/index.html,sha256=
|
68
|
+
pywebexec/templates/index.html,sha256=29-MBjeJ5mgYdrkRB27WiY0okQLYcFFycJvdCMuwvV0,3974
|
68
69
|
pywebexec/templates/popup.html,sha256=3kpMccKD_OLLhJ4Y9KRw6Ny8wQWjVaRrUfV9y5-bDiQ,1580
|
69
70
|
pywebexec/templates/swagger_ui.html,sha256=MAPr-z96VERAecDvX37V8q2Nxph-O0fNDBul1x2w9SI,1147
|
70
|
-
pywebexec-2.
|
71
|
-
pywebexec-2.
|
72
|
-
pywebexec-2.
|
73
|
-
pywebexec-2.
|
74
|
-
pywebexec-2.
|
75
|
-
pywebexec-2.
|
71
|
+
pywebexec-2.4.0.dist-info/licenses/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
|
72
|
+
pywebexec-2.4.0.dist-info/METADATA,sha256=of_DSpdHxXl41y6oQxQWaTvioxOEdYan-96zOHRIjz4,13015
|
73
|
+
pywebexec-2.4.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
74
|
+
pywebexec-2.4.0.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
|
75
|
+
pywebexec-2.4.0.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
|
76
|
+
pywebexec-2.4.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|