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 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
- command_status_cache = {}
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': 4,
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
- command_status_cache[command_id] = status
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
- if command_id in command_status_cache:
447
- return command_status_cache[command_id]
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 = json.load(f)
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
- last_line = status.get('last_output_line')
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
- last_line = get_last_line(output_file_path, status.get('cols'), status.get('rows'))
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': last_line,
598
+ 'last_output_line': status.get('last_output_line'),
585
599
  })
586
600
  return commands
587
601
 
@@ -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
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '1.6.17'
16
- __version_tuple__ = version_tuple = (1, 6, 17)
15
+ __version__ = version = '1.7.1'
16
+ __version_tuple__ = version_tuple = (1, 7, 1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pywebexec
3
- Version: 1.6.17
3
+ Version: 1.7.1
4
4
  Summary: Simple Python HTTP Exec Server
5
5
  Home-page: https://github.com/joknarf/pywebexec
6
6
  Author: Franck Jouvanceau
@@ -1,6 +1,6 @@
1
1
  pywebexec/__init__.py,sha256=4spIsVaF8RJt8S58AG_wWoORRNkws9Iwqprj27C3ljM,99
2
- pywebexec/pywebexec.py,sha256=mCNUbnttBIloCVmVl3jRuqA3kE6K9SlYLlDEAHb-dL0,32556
3
- pywebexec/version.py,sha256=bUNPwAsnC5Y4wwmtKw5lZfH0PuKwJg_38vSI9Pv11Qo,413
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=8CcIn-O4I1ojbiAYOeQl3yTnykn0Lj3xdWdx6m6HyNs,17085
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.6.17.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
37
- pywebexec-1.6.17.dist-info/METADATA,sha256=WOoqeA6-jB64mcwinFFIrJatxvesYYJAhXruiXmf0rA,8001
38
- pywebexec-1.6.17.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
39
- pywebexec-1.6.17.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
40
- pywebexec-1.6.17.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
41
- pywebexec-1.6.17.dist-info/RECORD,,
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,,