pywebexec 1.6.17__py3-none-any.whl → 1.7.1__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 +32 -18
- pywebexec/static/js/script.js +1 -1
- pywebexec/version.py +2 -2
- {pywebexec-1.6.17.dist-info → pywebexec-1.7.1.dist-info}/METADATA +1 -1
- {pywebexec-1.6.17.dist-info → pywebexec-1.7.1.dist-info}/RECORD +9 -9
- {pywebexec-1.6.17.dist-info → pywebexec-1.7.1.dist-info}/LICENSE +0 -0
- {pywebexec-1.6.17.dist-info → pywebexec-1.7.1.dist-info}/WHEEL +0 -0
- {pywebexec-1.6.17.dist-info → pywebexec-1.7.1.dist-info}/entry_points.txt +0 -0
- {pywebexec-1.6.17.dist-info → pywebexec-1.7.1.dist-info}/top_level.txt +0 -0
pywebexec/pywebexec.py
CHANGED
@@ -58,7 +58,7 @@ CONFDIR += "/.pywebexec"
|
|
58
58
|
term_command_id = str(uuid.uuid4())
|
59
59
|
|
60
60
|
# In-memory cache for command statuses
|
61
|
-
|
61
|
+
status_cache = {}
|
62
62
|
|
63
63
|
def generate_random_password(length=12):
|
64
64
|
characters = string.ascii_letters + string.digits + string.punctuation
|
@@ -162,7 +162,6 @@ def generate_selfsigned_cert(hostname, ip_addresses=None, key=None):
|
|
162
162
|
return cert_pem, key_pem
|
163
163
|
|
164
164
|
|
165
|
-
|
166
165
|
class PyWebExec(Application):
|
167
166
|
|
168
167
|
def __init__(self, app, options=None):
|
@@ -242,7 +241,8 @@ def start_gunicorn(daemonized=False, baselog=None):
|
|
242
241
|
accesslog = None #"-"
|
243
242
|
options = {
|
244
243
|
'bind': '%s:%s' % (args.listen, args.port),
|
245
|
-
'workers':
|
244
|
+
'workers': 1,
|
245
|
+
'threads': 4,
|
246
246
|
'timeout': 600,
|
247
247
|
'certfile': args.cert,
|
248
248
|
'keyfile': args.key,
|
@@ -429,6 +429,9 @@ def get_status_file_path(command_id):
|
|
429
429
|
def get_output_file_path(command_id):
|
430
430
|
return os.path.join(COMMAND_STATUS_DIR, f'{command_id}_output.txt')
|
431
431
|
|
432
|
+
def get_done_file_path(command_id):
|
433
|
+
return os.path.join(COMMAND_STATUS_DIR, f'{command_id}.done')
|
434
|
+
|
432
435
|
def update_command_status(command_id, updates):
|
433
436
|
status_file_path = get_status_file_path(command_id)
|
434
437
|
status = read_command_status(command_id) or {}
|
@@ -437,28 +440,32 @@ def update_command_status(command_id, updates):
|
|
437
440
|
output_file_path = get_output_file_path(command_id)
|
438
441
|
if os.path.exists(output_file_path):
|
439
442
|
status['last_output_line'] = get_last_line(output_file_path, status.get('cols'), status.get('rows'))
|
440
|
-
|
443
|
+
status_cache[command_id] = status
|
441
444
|
with open(status_file_path, 'w') as f:
|
442
445
|
json.dump(status, f)
|
443
|
-
|
446
|
+
if status.get('status') != 'running':
|
447
|
+
open(get_done_file_path(command_id), 'a').close()
|
448
|
+
|
449
|
+
|
444
450
|
def read_command_status(command_id):
|
445
451
|
# Return cached status if available
|
446
|
-
|
447
|
-
|
448
|
-
|
452
|
+
status_data = {}
|
453
|
+
if command_id in status_cache:
|
454
|
+
status_data = status_cache[command_id]
|
455
|
+
status = status_data.get('status')
|
456
|
+
if status and status != "running":
|
457
|
+
return status_data
|
458
|
+
if command_id in status_cache and not os.path.exists(get_done_file_path(command_id)):
|
459
|
+
return status_data
|
449
460
|
status_file_path = get_status_file_path(command_id)
|
450
461
|
if not os.path.exists(status_file_path):
|
451
462
|
return None
|
452
463
|
with open(status_file_path, 'r') as f:
|
453
464
|
try:
|
454
|
-
status_data
|
465
|
+
status_data.update(json.load(f))
|
455
466
|
except json.JSONDecodeError:
|
456
467
|
return None
|
457
|
-
|
458
|
-
# Cache the status if it is not "running"
|
459
|
-
if status_data['status'] != 'running':
|
460
|
-
command_status_cache[command_id] = status_data
|
461
|
-
|
468
|
+
status_cache[command_id] = status_data
|
462
469
|
return status_data
|
463
470
|
|
464
471
|
def sigwinch_passthrough(sig, data):
|
@@ -562,6 +569,7 @@ def command_str(command, params):
|
|
562
569
|
|
563
570
|
|
564
571
|
def read_commands():
|
572
|
+
global status_cache
|
565
573
|
commands = []
|
566
574
|
for filename in os.listdir(COMMAND_STATUS_DIR):
|
567
575
|
if filename.endswith('.json'):
|
@@ -569,11 +577,17 @@ def read_commands():
|
|
569
577
|
status = read_command_status(command_id)
|
570
578
|
if status:
|
571
579
|
command = command_str(status.get('command', '-'), status.get('params', []))
|
572
|
-
|
573
|
-
if status.get('status') == 'running':
|
580
|
+
if status.get('status') == 'running' and status.get('last_update',0)<datetime.now().timestamp()-5:
|
574
581
|
output_file_path = get_output_file_path(command_id)
|
575
582
|
if os.path.exists(output_file_path):
|
576
|
-
|
583
|
+
size = os.path.getsize(output_file_path)
|
584
|
+
if size != status.get('size'):
|
585
|
+
status.update({
|
586
|
+
'size': size,
|
587
|
+
'last_update': datetime.now().timestamp(),
|
588
|
+
'last_output_line': get_last_line(output_file_path, status.get('cols'), status.get('rows')),
|
589
|
+
})
|
590
|
+
status_cache[command_id] = status
|
577
591
|
commands.append({
|
578
592
|
'command_id': command_id,
|
579
593
|
'status': status.get('status'),
|
@@ -581,7 +595,7 @@ def read_commands():
|
|
581
595
|
'end_time': status.get('end_time', 'N/A'),
|
582
596
|
'command': command,
|
583
597
|
'exit_code': status.get('exit_code', 'N/A'),
|
584
|
-
'last_output_line':
|
598
|
+
'last_output_line': status.get('last_output_line'),
|
585
599
|
})
|
586
600
|
return commands
|
587
601
|
|
pywebexec/static/js/script.js
CHANGED
@@ -120,7 +120,7 @@ document.getElementById('launchForm').addEventListener('submit', async (event) =
|
|
120
120
|
throw new Error('Failed to launch command');
|
121
121
|
}
|
122
122
|
const data = await response.json();
|
123
|
-
await new Promise(r => setTimeout(r, 300))
|
123
|
+
//await new Promise(r => setTimeout(r, 300));// not ok
|
124
124
|
fetchCommands();
|
125
125
|
viewOutput(data.command_id);
|
126
126
|
commandInput.focus()
|
pywebexec/version.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
pywebexec/__init__.py,sha256=4spIsVaF8RJt8S58AG_wWoORRNkws9Iwqprj27C3ljM,99
|
2
|
-
pywebexec/pywebexec.py,sha256=
|
3
|
-
pywebexec/version.py,sha256=
|
2
|
+
pywebexec/pywebexec.py,sha256=yPag3ijHH1mzI6k6DjyvBms7FNAQ3UH_IPyeCHU-AO8,33323
|
3
|
+
pywebexec/version.py,sha256=kn-QYzzAhfbnfKK6EpE9gJz8TDZkEk52evaid1DHkG4,411
|
4
4
|
pywebexec/static/css/Consolas NF.ttf,sha256=DJEOzF0eqZ-kxu3Gs_VE8X0NJqiobBzmxWDGpdgGRxI,1313900
|
5
5
|
pywebexec/static/css/style.css,sha256=MJHUBpjWL4sLxM7a7DxypmPKaFJQbmA_ESNXsbLviNI,8201
|
6
6
|
pywebexec/static/css/xterm.css,sha256=uo5phWaUiJgcz0DAzv46uoByLLbJLeetYosL1xf68rY,5559
|
@@ -20,7 +20,7 @@ pywebexec/static/images/running.svg,sha256=fBCYwYb2O9K4N3waC2nURP25NRwZlqR4PbDZy
|
|
20
20
|
pywebexec/static/images/success.svg,sha256=NVwezvVMplt46ElW798vqGfrL21Mw_DWHUp_qiD_FU8,489
|
21
21
|
pywebexec/static/js/commands.js,sha256=h2fkd9qpypLBxvhEEbay23nwuqUwcKJA0vHugcyL8pU,7961
|
22
22
|
pywebexec/static/js/popup.js,sha256=I5TLYUm5s2p12a0VfFONOxxzhXLBmnpVzM-B5W-GPis,8294
|
23
|
-
pywebexec/static/js/script.js,sha256=
|
23
|
+
pywebexec/static/js/script.js,sha256=EPofQO0ec2bwIr2YdzzNY3dcXipW2_Mi2j34DIYvJeE,17096
|
24
24
|
pywebexec/static/js/xterm/LICENSE,sha256=EU1P4eXTull-_T9I80VuwnJXubB-zLzUl3xpEYj2T1M,1083
|
25
25
|
pywebexec/static/js/xterm/addon-canvas.js,sha256=ez6QTVvsmLVNJmdJlM-ZQ5bErwlxAQ_9DUmDIptl2TM,94607
|
26
26
|
pywebexec/static/js/xterm/addon-canvas.js.map,sha256=ECBA4B-BqUpdFeRzlsEWLSQnudnhLP-yPQJ8_hKquMo,379537
|
@@ -33,9 +33,9 @@ pywebexec/static/js/xterm/xterm.js.map,sha256=Y7O2Pb-fIS7Z8AC1D5s04_aiW_Jf1f4mCf
|
|
33
33
|
pywebexec/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
34
34
|
pywebexec/templates/index.html,sha256=5h8kLyzAhbvUDU9sEwrGIvD6FxAMcDZLXlN-ldlO8KU,2880
|
35
35
|
pywebexec/templates/popup.html,sha256=3ZqQcE9mYs-RXv0Lfb24zntOlvR137ZYI9mtCZNVAo0,1407
|
36
|
-
pywebexec-1.
|
37
|
-
pywebexec-1.
|
38
|
-
pywebexec-1.
|
39
|
-
pywebexec-1.
|
40
|
-
pywebexec-1.
|
41
|
-
pywebexec-1.
|
36
|
+
pywebexec-1.7.1.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
|
37
|
+
pywebexec-1.7.1.dist-info/METADATA,sha256=2qLB_f1jdH1RY5hctK3TZcD8_EofCTtsfLp0uYix0Is,8000
|
38
|
+
pywebexec-1.7.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
39
|
+
pywebexec-1.7.1.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
|
40
|
+
pywebexec-1.7.1.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
|
41
|
+
pywebexec-1.7.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|