pywebexec 1.9.1__py3-none-any.whl → 1.9.3__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
@@ -306,10 +306,10 @@ def print_urls(command_id=None):
306
306
  if token:
307
307
  url_params = f"?token={token}"
308
308
  if command_id:
309
- print(f"web popup: {protocol}://{hostname}:{args.port}/dopopup/{command_id}{url_params}", flush=True)
310
- print(f"web popup: {protocol}://{ip}:{args.port}/dopopup/{command_id}{url_params}", flush=True)
311
- print(f"raw output: {protocol}://{hostname}:{args.port}/command_output_raw/{command_id}{url_params}", flush=True)
312
- print(f"raw output: {protocol}://{ip}:{args.port}/command_output_raw/{command_id}{url_params}", flush=True)
309
+ print(f"web popup: {protocol}://{hostname}:{args.port}/commands/{command_id}/dopopup{url_params}", flush=True)
310
+ print(f"web popup: {protocol}://{ip}:{args.port}/commands/{command_id}/dopopup{url_params}", flush=True)
311
+ print(f"raw output: {protocol}://{hostname}:{args.port}/commands/{command_id}/output_raw{url_params}", flush=True)
312
+ print(f"raw output: {protocol}://{ip}:{args.port}/commands/{command_id}/output_raw{url_params}", flush=True)
313
313
  else:
314
314
  print(f"web commands: {protocol}://{hostname}:{args.port}{url_params}", flush=True)
315
315
  print(f"web commands: {protocol}://{ip}:{args.port}{url_params}", flush=True)
@@ -619,7 +619,7 @@ def log_error(fromip, user, message):
619
619
  def log_request(message):
620
620
  log_info(request.remote_addr, session.get('username', '-'), message)
621
621
 
622
- @app.route('/stop_command/<command_id>', methods=['POST'])
622
+ @app.route('/commands/<command_id>/stop', methods=['PATCH'])
623
623
  def stop_command(command_id):
624
624
  log_request(f"stop_command {command_id}")
625
625
  status = read_command_status(command_id)
@@ -629,7 +629,6 @@ def stop_command(command_id):
629
629
  pid = status['pid']
630
630
  end_time = datetime.now(timezone.utc).isoformat()
631
631
  try:
632
- #update_command_status(command_id, 'aborted', end_time=end_time, exit_code=-15)
633
632
  os.killpg(os.getpgid(pid), 15) # Send SIGTERM to the process group
634
633
  return jsonify({'message': 'Command aborted'})
635
634
  except Exception as e:
@@ -704,7 +703,7 @@ def verify_ldap(username, password):
704
703
  print(f"LDAP authentication failed: {e}")
705
704
  return False
706
705
 
707
- @app.route('/run_command', methods=['POST'])
706
+ @app.route('/commands', methods=['POST'])
708
707
  def run_command_endpoint():
709
708
  data = request.json
710
709
  command = data.get('command')
@@ -745,7 +744,7 @@ def run_command_endpoint():
745
744
 
746
745
  return jsonify({'message': 'Command is running', 'command_id': command_id})
747
746
 
748
- @app.route('/command_status/<command_id>', methods=['GET'])
747
+ @app.route('/commands/<command_id>', methods=['GET'])
749
748
  def get_command_status(command_id):
750
749
  status = read_command_status(command_id)
751
750
  if not status:
@@ -763,7 +762,7 @@ def list_commands():
763
762
  commands.sort(key=lambda x: x['start_time'], reverse=True)
764
763
  return jsonify(commands)
765
764
 
766
- @app.route('/command_output/<command_id>', methods=['GET'])
765
+ @app.route('/commands/<command_id>/output', methods=['GET'])
767
766
  def get_command_output(command_id):
768
767
  offset = int(request.args.get('offset', 0))
769
768
  maxsize = int(request.args.get('maxsize', 10485760))
@@ -787,7 +786,7 @@ def get_command_output(command_id):
787
786
  'cols': status_data.get("cols"),
788
787
  'rows': status_data.get("rows"),
789
788
  'links': {
790
- 'next': f'{request.url_root}command_output/{command_id}?offset={new_offset}&maxsize={maxsize}{token_param}'
789
+ 'next': f'{request.url_root}commands/{command_id}/output?offset={new_offset}&maxsize={maxsize}{token_param}'
791
790
  }
792
791
  }
793
792
  if request.headers.get('Accept') == 'text/plain':
@@ -795,7 +794,7 @@ def get_command_output(command_id):
795
794
  return jsonify(response)
796
795
  return jsonify({'error': 'Invalid command_id'}), 404
797
796
 
798
- @app.route('/command_output_raw/<command_id>', methods=['GET'])
797
+ @app.route('/commands/<command_id>/output_raw', methods=['GET'])
799
798
  def get_command_output_raw(command_id):
800
799
  offset = int(request.args.get('offset', 0))
801
800
  @stream_with_context
@@ -825,11 +824,11 @@ def list_executables():
825
824
  executables.sort() # Sort the list of executables alphabetically
826
825
  return jsonify(executables)
827
826
 
828
- @app.route('/popup/<command_id>')
827
+ @app.route('/commands/<command_id>/popup')
829
828
  def popup(command_id):
830
829
  return render_template('popup.html', command_id=command_id)
831
830
 
832
- @app.route('/dopopup/<command_id>')
831
+ @app.route('/commands/<command_id>/dopopup')
833
832
  def do_popup(command_id):
834
833
  token = request.args.get('token', '')
835
834
  token_param = f'?token={token}' if token else ''
@@ -838,7 +837,7 @@ def do_popup(command_id):
838
837
  <head>
839
838
  <script type="text/javascript">
840
839
  window.onload = function() {{
841
- window.open('/popup/{command_id}{token_param}', '_blank', 'width=1000,height=600');
840
+ window.open('/commands/{command_id}/popup{token_param}', '_blank', 'width=1000,height=600');
842
841
  window.close();
843
842
  }};
844
843
  </script>
@@ -216,7 +216,7 @@ window.addEventListener('load', () => {
216
216
 
217
217
  async function fetchExecutables() {
218
218
  try {
219
- const response = await fetch(`/executables${urlToken}`);
219
+ const response = await fetch(`/executables`);
220
220
  if (!response.ok) {
221
221
  throw new Error('Failed to fetch command status');
222
222
  }
@@ -98,11 +98,6 @@ function autoFit(scroll=true) {
98
98
  if (scroll) terminal.scrollToBottom();
99
99
  }
100
100
 
101
- function getTokenParam() {
102
- const urlParams = new URLSearchParams(window.location.search);
103
- return urlParams.get('token') ? `?token=${urlParams.get('token')}` : '';
104
- }
105
- const urlToken = getTokenParam();
106
101
 
107
102
 
108
103
  function setCommandStatus(status) {
@@ -161,13 +156,14 @@ async function viewOutput(command_id) {
161
156
  slider.value = 1000;
162
157
  adjustOutputHeight();
163
158
  currentCommandId = command_id;
164
- nextOutputLink = `/command_output/${command_id}${urlToken}`;
159
+ nextOutputLink = `/commands/${command_id}/output`;
165
160
  clearInterval(outputInterval);
166
161
  terminal.clear();
167
162
  terminal.reset();
168
163
  fullOutput = '';
169
164
  try {
170
- const response = await fetch(`/command_status/${command_id}${urlToken}`);
165
+ // Updated endpoint below:
166
+ const response = await fetch(`/commands/${command_id}`);
171
167
  if (!response.ok) {
172
168
  return;
173
169
  }
@@ -256,7 +252,7 @@ window.addEventListener('load', () => {
256
252
  slider = document.getElementById('outputSlider');
257
253
  slider.addEventListener('input', sliderUpdateOutput);
258
254
  adjustOutputHeight();
259
- const commandId = window.location.pathname.split('/').slice(-1)[0];
255
+ const commandId = window.location.pathname.split('/').slice(-2)[0];
260
256
  viewOutput(commandId);
261
257
  });
262
258
 
@@ -108,12 +108,6 @@ function autoFit(scroll=true) {
108
108
  if (scroll) terminal.scrollToBottom();
109
109
  }
110
110
 
111
- function getTokenParam() {
112
- const urlParams = new URLSearchParams(window.location.search);
113
- return urlParams.get('token') ? `?token=${urlParams.get('token')}` : '';
114
- }
115
- const urlToken = getTokenParam();
116
-
117
111
 
118
112
  document.getElementById('launchForm').addEventListener('submit', async (event) => {
119
113
  event.preventDefault();
@@ -122,7 +116,7 @@ document.getElementById('launchForm').addEventListener('submit', async (event) =
122
116
  fitAddon.fit();
123
117
  terminal.clear();
124
118
  try {
125
- const response = await fetch(`/run_command${urlToken}`, {
119
+ const response = await fetch(`/commands`, {
126
120
  method: 'POST',
127
121
  headers: {
128
122
  'Content-Type': 'application/json'
@@ -133,7 +127,6 @@ document.getElementById('launchForm').addEventListener('submit', async (event) =
133
127
  throw new Error('Failed to launch command');
134
128
  }
135
129
  const data = await response.json();
136
- //await new Promise(r => setTimeout(r, 300));// not ok
137
130
  viewOutput(data.command_id);
138
131
  fetchCommands();
139
132
  commandInput.focus()
@@ -145,7 +138,7 @@ document.getElementById('launchForm').addEventListener('submit', async (event) =
145
138
 
146
139
  async function fetchCommands(hide=false) {
147
140
  try {
148
- const response = await fetch(`/commands${urlToken}`);
141
+ const response = await fetch(`/commands`);
149
142
  if (!response.ok) {
150
143
  document.getElementById('dimmer').style.display = 'block';
151
144
  return;
@@ -253,13 +246,13 @@ async function viewOutput(command_id) {
253
246
  outputPercentage.innerText = '100%';
254
247
  adjustOutputHeight();
255
248
  currentCommandId = command_id;
256
- nextOutputLink = `/command_output/${command_id}${urlToken}`;
249
+ nextOutputLink = `/commands/${command_id}/output`;
257
250
  clearInterval(outputInterval);
258
251
  terminal.clear();
259
252
  terminal.reset();
260
253
  fullOutput = '';
261
254
  try {
262
- const response = await fetch(`/command_status/${command_id}${urlToken}`);
255
+ const response = await fetch(`/commands/${command_id}`);
263
256
  if (!response.ok) {
264
257
  outputInterval = setInterval(() => fetchOutput(nextOutputLink), 500);
265
258
  }
@@ -290,7 +283,7 @@ async function viewOutput(command_id) {
290
283
  async function openPopup(command_id, event) {
291
284
  event.stopPropagation();
292
285
  event.stopImmediatePropagation();
293
- const popupUrl = `/popup/${command_id}${urlToken}`;
286
+ const popupUrl = `/commands/${command_id}/popup`;
294
287
  window.open(popupUrl, '_blank', 'width=1000,height=600');
295
288
  }
296
289
 
@@ -298,7 +291,7 @@ async function relaunchCommand(command_id, event) {
298
291
  event.stopPropagation();
299
292
  event.stopImmediatePropagation();
300
293
  try {
301
- const response = await fetch(`/command_status/${command_id}${urlToken}`);
294
+ const response = await fetch(`/commands/${command_id}`);
302
295
  if (!response.ok) {
303
296
  throw new Error('Failed to fetch command status');
304
297
  }
@@ -309,7 +302,7 @@ async function relaunchCommand(command_id, event) {
309
302
  }
310
303
  fitAddon.fit();
311
304
  terminal.clear();
312
- const relaunchResponse = await fetch(`/run_command${urlToken}`, {
305
+ const relaunchResponse = await fetch(`/commands`, {
313
306
  method: 'POST',
314
307
  headers: {
315
308
  'Content-Type': 'application/json'
@@ -337,8 +330,8 @@ async function stopCommand(command_id, event) {
337
330
  event.stopPropagation();
338
331
  event.stopImmediatePropagation();
339
332
  try {
340
- const response = await fetch(`/stop_command/${command_id}${urlToken}`, {
341
- method: 'POST'
333
+ const response = await fetch(`/commands/${command_id}/stop`, {
334
+ method: 'PATCH'
342
335
  });
343
336
  if (!response.ok) {
344
337
  throw new Error('Failed to stop command');
pywebexec/swagger.yaml CHANGED
@@ -3,7 +3,12 @@ info:
3
3
  title: PyWebExec API
4
4
  version: "1.0"
5
5
  paths:
6
- /run_command:
6
+ /commands:
7
+ get:
8
+ summary: "List commands status"
9
+ responses:
10
+ "200":
11
+ description: "List of all commands status"
7
12
  post:
8
13
  summary: "Run a command"
9
14
  consumes:
@@ -29,7 +34,7 @@ paths:
29
34
  responses:
30
35
  "200":
31
36
  description: "Command started"
32
- /command_status/{command_id}:
37
+ /commands/{command_id}:
33
38
  get:
34
39
  summary: "Get command status"
35
40
  parameters:
@@ -40,15 +45,39 @@ paths:
40
45
  responses:
41
46
  "200":
42
47
  description: "Command status returned"
43
- /commands:
48
+ /commands/{command_id}/output:
44
49
  get:
45
- summary: "List commands"
50
+ summary: "Get command output"
51
+ parameters:
52
+ - in: path
53
+ name: command_id
54
+ required: true
55
+ type: string
56
+ - in: query
57
+ name: offset
58
+ type: integer
59
+ default: 0
60
+ - in: query
61
+ name: maxsize
62
+ type: integer
63
+ default: 10485760
64
+ responses:
65
+ "200":
66
+ description: "Command output returned"
67
+ /commands/{command_id}/stop:
68
+ patch:
69
+ summary: "Stop a running command"
70
+ parameters:
71
+ - in: path
72
+ name: command_id
73
+ required: true
74
+ type: string
46
75
  responses:
47
76
  "200":
48
- description: "List of all commands"
77
+ description: "Command stopped successfully"
49
78
  /executables:
50
79
  get:
51
- summary: "List executables"
80
+ summary: "List available executable commands"
52
81
  responses:
53
82
  "200":
54
83
  description: "List of executables returned as an array of executable names"
pywebexec/version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '1.9.1'
21
- __version_tuple__ = version_tuple = (1, 9, 1)
20
+ __version__ = version = '1.9.3'
21
+ __version_tuple__ = version_tuple = (1, 9, 3)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pywebexec
3
- Version: 1.9.1
3
+ Version: 1.9.3
4
4
  Summary: Simple Python HTTP Exec Server
5
5
  Home-page: https://github.com/joknarf/pywebexec
6
6
  Author: Franck Jouvanceau
@@ -200,10 +200,10 @@ $ curl http://myhost:8080/command_output/<command_id> -H "Accept: text/plain"
200
200
 
201
201
  | method | route | params/payload | returns
202
202
  |-----------|-----------------------------|--------------------|---------------------|
203
- | POST | /run_command | command: str<br>params: array[str]<br>rows: int<br>cols: int | command_id: uuid<br>message: str |
204
- | POST | /stop_command/command_id | | message: str |
205
- | GET | /command_status/command_id | | command_id: uuid<br>command: str<br>params: array[str]<br>start_time: isotime<br>end_time: isotime<br>status: str<br>exit_code: int<br>last_output_line: str |
206
- | GET | /commands | | array of<br>command_id: uuid<br>command: str<br>start_time: isotime<br>end_time: isotime<br>status: str<br>exit_code: int<br>last_output_line: str |
207
203
  | GET | /executables | | array of str |
208
- | GET | /command_output/command_id | offset: int | output: str<br>status: str<br>links: { next: str } |
209
- | GET | /command_output_raw/command_id | offset: int | output: stream raw output until end of command<br>curl -Ns http://srv/command_output_raw/command_id|
204
+ | GET | /commands | | array of<br>command_id: uuid<br>command: str<br>start_time: isotime<br>end_time: isotime<br>status: str<br>exit_code: int<br>last_output_line: str |
205
+ | GET | /commands/{id} | | command_id: uuid<br>command: str<br>params: array[str]<br>start_time: isotime<br>end_time: isotime<br>status: str<br>exit_code: int<br>last_output_line: str |
206
+ | GET | /commands/{id}/output | offset: int | output: str<br>status: str<br>links: { next: str } |
207
+ | GET | /commands/{id}/output_raw | offset: int | output: stream raw output until end of command<br>curl -Ns http://srv/commands/{id}/output_raw|
208
+ | POST | /commands | command: str<br>params: array[str]<br>rows: int<br>cols: int | command_id: uuid<br>message: str |
209
+ | PATCH | /commands/{id}/stop | | message: str |
@@ -1,8 +1,8 @@
1
1
  pywebexec/__init__.py,sha256=197fHJy0UDBwTTpGCGortZRr-w2kTaD7MxqdbVmTEi0,61
2
2
  pywebexec/host_ip.py,sha256=Ud_HTflWVQ8789aoQ2RZdT1wGI-ccvrwSWGz_c7T3TI,1241
3
- pywebexec/pywebexec.py,sha256=VJH7C8MI18Ybx0KHrPiBsVy17xpO4pfd_KXoNIkVBxg,33724
4
- pywebexec/swagger.yaml,sha256=0AI4KfQI5ezTF8PP4E_ER6dfiseBi3SiOk6OUj5DjcA,1253
5
- pywebexec/version.py,sha256=0TTXzdumH9yE7lSKUOdvHb1HJBaZX7fxyxuZNWj70Cg,511
3
+ pywebexec/pywebexec.py,sha256=uGOdA0nQ4Na9F12vZYXVBr11Uk9XoYuhdM8lJxEUC0Y,33679
4
+ pywebexec/swagger.yaml,sha256=-uafngZxQFHLdnWY-9SFCdgotO5wynFN2sTEyuBpQ_Q,1998
5
+ pywebexec/version.py,sha256=9iRyJjXIu4ospHHgY6jThnBbx_i0E46WrhiFzzJdLrE,511
6
6
  pywebexec/static/css/style.css,sha256=SuOU_USRh8BiAxEJ1LDYIx3asf3lETu_evWzA54gsBo,8145
7
7
  pywebexec/static/css/xterm.css,sha256=uo5phWaUiJgcz0DAzv46uoByLLbJLeetYosL1xf68rY,5559
8
8
  pywebexec/static/fonts/CommitMonoNerdFontMono-Regular.ttf,sha256=v6nZdSx5cs_TIic8Fujrjzg9u9glWjorDIr7RlwNceM,2370228
@@ -23,9 +23,9 @@ pywebexec/static/images/popup.svg,sha256=0Bl9A_v5cBsMPn6FnOlVWlAQKgd2zqiWQbhjcL9
23
23
  pywebexec/static/images/resume.svg,sha256=99LP1Ya2JXakRCO9kW8JMuT_4a_CannF65EiuwtvK4A,607
24
24
  pywebexec/static/images/running.svg,sha256=fBCYwYb2O9K4N3waC2nURP25NRwZlqR4PbDZy6JQMww,610
25
25
  pywebexec/static/images/success.svg,sha256=NVwezvVMplt46ElW798vqGfrL21Mw_DWHUp_qiD_FU8,489
26
- pywebexec/static/js/commands.js,sha256=TmfcauQlfIeAeC8pwQvKspc4PA_VYLbPTnVCDVBZ87I,8420
27
- pywebexec/static/js/popup.js,sha256=pYBwMuGnOCz4O1O-LmxHNlqHiZCn55ozbYchQraJp6Q,9222
28
- pywebexec/static/js/script.js,sha256=iQcl-22b4GmGLw8lY2aviC-yd8dTYZKi8cN-KoaYwvE,18216
26
+ pywebexec/static/js/commands.js,sha256=VhfRJsMqXIwrTRCkEhGzDuaG5WslmTGCcrEo_eqfl1w,8409
27
+ pywebexec/static/js/popup.js,sha256=0fr3pp4j9D2fXEVnHyQrx2bPWFHfgbb336dbewgH1d8,9023
28
+ pywebexec/static/js/script.js,sha256=ZtVBu2CtH5XgHIF9nflGNw-Aq26LXzYGmyd51MHrAHY,17851
29
29
  pywebexec/static/js/xterm/LICENSE,sha256=EU1P4eXTull-_T9I80VuwnJXubB-zLzUl3xpEYj2T1M,1083
30
30
  pywebexec/static/js/xterm/addon-canvas.js,sha256=ez6QTVvsmLVNJmdJlM-ZQ5bErwlxAQ_9DUmDIptl2TM,94607
31
31
  pywebexec/static/js/xterm/addon-canvas.js.map,sha256=ECBA4B-BqUpdFeRzlsEWLSQnudnhLP-yPQJ8_hKquMo,379537
@@ -40,9 +40,9 @@ pywebexec/static/js/xterm/xterm.js.map,sha256=Y7O2Pb-fIS7Z8AC1D5s04_aiW_Jf1f4mCf
40
40
  pywebexec/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
41
  pywebexec/templates/index.html,sha256=VLcuC0RUkwefDugXWcXsjd5C3owKk5wCJoYIo48xbgk,3106
42
42
  pywebexec/templates/popup.html,sha256=3kpMccKD_OLLhJ4Y9KRw6Ny8wQWjVaRrUfV9y5-bDiQ,1580
43
- pywebexec-1.9.1.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
44
- pywebexec-1.9.1.dist-info/METADATA,sha256=xo0Wz-wbREaii01oK9ro9ObeOWIIV4Zf40_5a74EGkI,8186
45
- pywebexec-1.9.1.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
46
- pywebexec-1.9.1.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
47
- pywebexec-1.9.1.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
48
- pywebexec-1.9.1.dist-info/RECORD,,
43
+ pywebexec-1.9.3.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
44
+ pywebexec-1.9.3.dist-info/METADATA,sha256=AaRXJqL-SrwHOVX5XQ4Rdxdpw0h46jL2YHE4awGcFnU,8154
45
+ pywebexec-1.9.3.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
46
+ pywebexec-1.9.3.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
47
+ pywebexec-1.9.3.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
48
+ pywebexec-1.9.3.dist-info/RECORD,,