pywebexec 1.1.10__tar.gz → 1.1.12__tar.gz

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.
Files changed (28) hide show
  1. {pywebexec-1.1.10/pywebexec.egg-info → pywebexec-1.1.12}/PKG-INFO +1 -1
  2. {pywebexec-1.1.10 → pywebexec-1.1.12}/pywebexec/static/css/style.css +26 -0
  3. pywebexec-1.1.12/pywebexec/static/js/script.js +255 -0
  4. {pywebexec-1.1.10 → pywebexec-1.1.12}/pywebexec/templates/index.html +3 -0
  5. {pywebexec-1.1.10 → pywebexec-1.1.12}/pywebexec/version.py +2 -2
  6. {pywebexec-1.1.10 → pywebexec-1.1.12/pywebexec.egg-info}/PKG-INFO +1 -1
  7. pywebexec-1.1.10/pywebexec/static/js/script.js +0 -198
  8. {pywebexec-1.1.10 → pywebexec-1.1.12}/.github/workflows/python-publish.yml +0 -0
  9. {pywebexec-1.1.10 → pywebexec-1.1.12}/.gitignore +0 -0
  10. {pywebexec-1.1.10 → pywebexec-1.1.12}/LICENSE +0 -0
  11. {pywebexec-1.1.10 → pywebexec-1.1.12}/README.md +0 -0
  12. {pywebexec-1.1.10 → pywebexec-1.1.12}/pyproject.toml +0 -0
  13. {pywebexec-1.1.10 → pywebexec-1.1.12}/pywebexec/__init__.py +0 -0
  14. {pywebexec-1.1.10 → pywebexec-1.1.12}/pywebexec/pywebexec.py +0 -0
  15. {pywebexec-1.1.10 → pywebexec-1.1.12}/pywebexec/static/images/aborted.svg +0 -0
  16. {pywebexec-1.1.10 → pywebexec-1.1.12}/pywebexec/static/images/copy.svg +0 -0
  17. {pywebexec-1.1.10 → pywebexec-1.1.12}/pywebexec/static/images/copy_ok.svg +0 -0
  18. {pywebexec-1.1.10 → pywebexec-1.1.12}/pywebexec/static/images/failed.svg +0 -0
  19. {pywebexec-1.1.10 → pywebexec-1.1.12}/pywebexec/static/images/favicon.svg +0 -0
  20. {pywebexec-1.1.10 → pywebexec-1.1.12}/pywebexec/static/images/running.gif +0 -0
  21. {pywebexec-1.1.10 → pywebexec-1.1.12}/pywebexec/static/images/success.svg +0 -0
  22. {pywebexec-1.1.10 → pywebexec-1.1.12}/pywebexec/templates/__init__.py +0 -0
  23. {pywebexec-1.1.10 → pywebexec-1.1.12}/pywebexec.egg-info/SOURCES.txt +0 -0
  24. {pywebexec-1.1.10 → pywebexec-1.1.12}/pywebexec.egg-info/dependency_links.txt +0 -0
  25. {pywebexec-1.1.10 → pywebexec-1.1.12}/pywebexec.egg-info/entry_points.txt +0 -0
  26. {pywebexec-1.1.10 → pywebexec-1.1.12}/pywebexec.egg-info/requires.txt +0 -0
  27. {pywebexec-1.1.10 → pywebexec-1.1.12}/pywebexec.egg-info/top_level.txt +0 -0
  28. {pywebexec-1.1.10 → pywebexec-1.1.12}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pywebexec
3
- Version: 1.1.10
3
+ Version: 1.1.12
4
4
  Summary: Simple Python HTTP Exec Server
5
5
  Home-page: https://github.com/joknarf/pywebexec
6
6
  Author: Franck Jouvanceau
@@ -139,3 +139,29 @@ input {
139
139
  tr.clickable-row {
140
140
  cursor: pointer;
141
141
  }
142
+ body.dimmed {
143
+ background-color: rgba(0, 0, 0, 0.5);
144
+ pointer-events: none;
145
+ }
146
+ body.dimmed * {
147
+ pointer-events: none;
148
+ }
149
+ .dimmer {
150
+ display: none;
151
+ position: fixed;
152
+ top: 0;
153
+ left: 0;
154
+ width: 100%;
155
+ height: 100%;
156
+ background-color: rgba(0, 0, 0, 0.5);
157
+ z-index: 1000;
158
+ }
159
+ .dimmer-text {
160
+ color: white;
161
+ font-size: 24px;
162
+ text-align: center;
163
+ position: absolute;
164
+ top: 50%;
165
+ left: 50%;
166
+ transform: translate(-50%, -50%);
167
+ }
@@ -0,0 +1,255 @@
1
+ let currentCommandId = null;
2
+ let outputInterval = null;
3
+
4
+ document.getElementById('launchForm').addEventListener('submit', async (event) => {
5
+ event.preventDefault();
6
+ const commandName = document.getElementById('commandName').value;
7
+ const params = document.getElementById('params').value.split(' ');
8
+ try {
9
+ const response = await fetch('/run_command', {
10
+ method: 'POST',
11
+ headers: {
12
+ 'Content-Type': 'application/json'
13
+ },
14
+ body: JSON.stringify({ command: commandName, params: params })
15
+ });
16
+ if (!response.ok) {
17
+ throw new Error('Failed to launch command');
18
+ }
19
+ const data = await response.json();
20
+ fetchCommands();
21
+ viewOutput(data.command_id);
22
+ } catch (error) {
23
+ console.log('Error running command:', error);
24
+ }
25
+ });
26
+
27
+ async function fetchCommands() {
28
+ try {
29
+ const response = await fetch('/commands');
30
+ if (!response.ok) {
31
+ document.getElementById('dimmer').style.display = 'block';
32
+ return;
33
+ }
34
+ const commands = await response.json();
35
+ commands.sort((a, b) => new Date(b.start_time) - new Date(a.start_time));
36
+ const commandsTbody = document.getElementById('commands');
37
+ commandsTbody.innerHTML = '';
38
+ if (!currentCommandId && commands.length) {
39
+ currentCommandId = commands[0].command_id;
40
+ viewOutput(currentCommandId);
41
+ }
42
+ commands.forEach(command => {
43
+ const commandRow = document.createElement('tr');
44
+ commandRow.className = `clickable-row ${command.command_id === currentCommandId ? 'currentcommand' : ''}`;
45
+ commandRow.onclick = () => viewOutput(command.command_id);
46
+ commandRow.innerHTML = `
47
+ <td class="monospace">
48
+ ${navigator.clipboard == undefined ? `${command.command_id}` : `<span class="copy_clip" onclick="copyToClipboard('${command.command_id.slice(0, 8)}', this, event)">${command.command_id.slice(0, 8)}</span>`}
49
+ </td>
50
+ <td><span class="status-icon status-${command.status}"></span>${command.status}</td>
51
+ <td>${formatTime(command.start_time)}</td>
52
+ <td>${command.status === 'running' ? formatDuration(command.start_time, new Date().toISOString()) : formatDuration(command.start_time, command.end_time)}</td>
53
+ <td>${command.exit_code}</td>
54
+ <td>${command.command.replace(/^\.\//, '')}</td>
55
+ <td>
56
+ ${command.status === 'running' ? `<button onclick="stopCommand('${command.command_id}')">Stop</button>` : `<button onclick="relaunchCommand('${command.command_id}')">Run</button>`}
57
+ </td>
58
+ <td class="monospace outcol">${command.last_output_line || ''}</td>
59
+ `;
60
+ commandsTbody.appendChild(commandRow);
61
+ });
62
+ document.getElementById('dimmer').style.display = 'none';
63
+ } catch (error) {
64
+ console.log('Error fetching commands:', error);
65
+ }
66
+ }
67
+
68
+ async function fetchExecutables() {
69
+ try {
70
+ const response = await fetch('/executables');
71
+ if (!response.ok) {
72
+ throw new Error('Failed to fetch command status');
73
+ }
74
+ const executables = await response.json();
75
+ const commandNameSelect = document.getElementById('commandName');
76
+ commandNameSelect.innerHTML = '';
77
+ executables.forEach(executable => {
78
+ const option = document.createElement('option');
79
+ option.value = executable;
80
+ option.textContent = executable;
81
+ commandNameSelect.appendChild(option);
82
+ });
83
+ } catch (error) {
84
+ console.log('Error fetching executables:', error);
85
+ alert("Failed to fetch executables");
86
+ }
87
+ }
88
+
89
+ async function fetchOutput(command_id) {
90
+ try {
91
+ const outputDiv = document.getElementById('output');
92
+ const response = await fetch(`/command_output/${command_id}`);
93
+ if (!response.ok) {
94
+ return;
95
+ }
96
+ const data = await response.json();
97
+ if (data.error) {
98
+ outputDiv.innerHTML = data.error;
99
+ clearInterval(outputInterval);
100
+ } else {
101
+ outputDiv.innerHTML = data.output;
102
+ outputDiv.scrollTop = outputDiv.scrollHeight;
103
+ if (data.status != 'running') {
104
+ clearInterval(outputInterval);
105
+ }
106
+ }
107
+ } catch (error) {
108
+ console.log('Error fetching output:', error);
109
+ }
110
+ }
111
+
112
+ async function viewOutput(command_id) {
113
+ adjustOutputHeight();
114
+ currentCommandId = command_id;
115
+ clearInterval(outputInterval);
116
+ try {
117
+ const response = await fetch(`/command_status/${command_id}`);
118
+ if (!response.ok) {
119
+ return;
120
+ }
121
+ const data = await response.json();
122
+ if (data.status === 'running') {
123
+ fetchOutput(command_id);
124
+ outputInterval = setInterval(() => fetchOutput(command_id), 1000);
125
+ } else {
126
+ fetchOutput(command_id);
127
+ }
128
+ fetchCommands(); // Refresh the command list to highlight the current command
129
+ } catch (error) {
130
+ console.log('Error viewing output:', error);
131
+ }
132
+ }
133
+
134
+ async function relaunchCommand(command_id) {
135
+ try {
136
+ const response = await fetch(`/command_status/${command_id}`);
137
+ if (!response.ok) {
138
+ throw new Error('Failed to fetch command status');
139
+ }
140
+ const data = await response.json();
141
+ if (data.error) {
142
+ alert(data.error);
143
+ return;
144
+ }
145
+ const relaunchResponse = await fetch('/run_command', {
146
+ method: 'POST',
147
+ headers: {
148
+ 'Content-Type': 'application/json'
149
+ },
150
+ body: JSON.stringify({
151
+ command: data.command,
152
+ params: data.params
153
+ })
154
+ });
155
+ if (!relaunchResponse.ok) {
156
+ throw new Error('Failed to relaunch command');
157
+ }
158
+ const relaunchData = await relaunchResponse.json();
159
+ fetchCommands();
160
+ viewOutput(relaunchData.command_id);
161
+ } catch (error) {
162
+ console.log('Error relaunching command:', error);
163
+ alert('Failed to relaunch command. Please try again.');
164
+ }
165
+ }
166
+
167
+ async function stopCommand(command_id) {
168
+ try {
169
+ const response = await fetch(`/stop_command/${command_id}`, {
170
+ method: 'POST'
171
+ });
172
+ if (!response.ok) {
173
+ throw new Error('Failed to stop command');
174
+ }
175
+ const data = await response.json();
176
+ if (data.error) {
177
+ alert(data.error);
178
+ } else {
179
+ alert(data.message);
180
+ fetchCommands();
181
+ }
182
+ } catch (error) {
183
+ console.log('Error stopping command:', error);
184
+ alert('Failed to stop command. Please try again.');
185
+ }
186
+ }
187
+
188
+ function formatTime(time) {
189
+ if (!time || time === 'N/A') return 'N/A';
190
+ const date = new Date(time);
191
+ return date.toISOString().slice(0, 16).replace('T', ' ');
192
+ }
193
+
194
+ function formatDuration(startTime, endTime) {
195
+ if (!startTime || !endTime) return 'N/A';
196
+ const start = new Date(startTime);
197
+ const end = new Date(endTime);
198
+ const duration = (end - start) / 1000;
199
+ const hours = Math.floor(duration / 3600);
200
+ const minutes = Math.floor((duration % 3600) / 60);
201
+ const seconds = Math.floor(duration % 60);
202
+ return `${hours}h ${minutes}m ${seconds}s`;
203
+ }
204
+
205
+ function copyToClipboard(text, element, event) {
206
+ event.stopPropagation();
207
+ event.stopImmediatePropagation();
208
+ navigator.clipboard.writeText(text).then(() => {
209
+ element.classList.add('copy_clip_ok');
210
+ setTimeout(() => {
211
+ element.classList.remove('copy_clip_ok');
212
+ }, 1000);
213
+ });
214
+ }
215
+
216
+ function adjustOutputHeight() {
217
+ const outputDiv = document.getElementById('output');
218
+ const windowHeight = window.innerHeight;
219
+ const outputTop = outputDiv.getBoundingClientRect().top;
220
+ const maxHeight = windowHeight - outputTop - 30; // 20px for padding/margin
221
+ outputDiv.style.maxHeight = `${maxHeight}px`;
222
+ }
223
+
224
+ function initResizer() {
225
+ const resizer = document.getElementById('resizer');
226
+ const tableContainer = document.getElementById('tableContainer');
227
+ let startY, startHeight;
228
+
229
+ resizer.addEventListener('mousedown', (e) => {
230
+ startY = e.clientY;
231
+ startHeight = parseInt(document.defaultView.getComputedStyle(tableContainer).height, 10);
232
+ document.documentElement.addEventListener('mousemove', doDrag, false);
233
+ document.documentElement.addEventListener('mouseup', stopDrag, false);
234
+ });
235
+
236
+ function doDrag(e) {
237
+ tableContainer.style.height = `${startHeight + e.clientY - startY}px`;
238
+ adjustOutputHeight();
239
+ }
240
+
241
+ function stopDrag() {
242
+ document.documentElement.removeEventListener('mousemove', doDrag, false);
243
+ document.documentElement.removeEventListener('mouseup', stopDrag, false);
244
+ }
245
+ }
246
+
247
+ window.addEventListener('resize', adjustOutputHeight);
248
+ window.addEventListener('load', () => {
249
+ adjustOutputHeight();
250
+ initResizer();
251
+ });
252
+
253
+ fetchExecutables();
254
+ fetchCommands();
255
+ setInterval(fetchCommands, 5000);
@@ -7,6 +7,9 @@
7
7
  <link rel="stylesheet" href="/static/css/style.css">
8
8
  </head>
9
9
  <body>
10
+ <div id="dimmer" class="dimmer">
11
+ <div class="dimmer-text">Server not reachable</div>
12
+ </div>
10
13
  <h2><span class="status-icon title-icon"></span>{{ title }}</h2>
11
14
  <form id="launchForm">
12
15
  <label for="commandName">Command</label>
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '1.1.10'
16
- __version_tuple__ = version_tuple = (1, 1, 10)
15
+ __version__ = version = '1.1.12'
16
+ __version_tuple__ = version_tuple = (1, 1, 12)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pywebexec
3
- Version: 1.1.10
3
+ Version: 1.1.12
4
4
  Summary: Simple Python HTTP Exec Server
5
5
  Home-page: https://github.com/joknarf/pywebexec
6
6
  Author: Franck Jouvanceau
@@ -1,198 +0,0 @@
1
- let currentCommandId = null;
2
- let outputInterval = null;
3
-
4
- document.getElementById('launchForm').addEventListener('submit', async (event) => {
5
- event.preventDefault();
6
- const commandName = document.getElementById('commandName').value;
7
- const params = document.getElementById('params').value.split(' ');
8
- const response = await fetch('/run_command', {
9
- method: 'POST',
10
- headers: {
11
- 'Content-Type': 'application/json'
12
- },
13
- body: JSON.stringify({ command: commandName, params: params })
14
- });
15
- const data = await response.json();
16
- fetchCommands();
17
- viewOutput(data.command_id);
18
- });
19
-
20
- async function fetchCommands() {
21
- const response = await fetch('/commands');
22
- const commands = await response.json();
23
- commands.sort((a, b) => new Date(b.start_time) - new Date(a.start_time));
24
- const commandsTbody = document.getElementById('commands');
25
- commandsTbody.innerHTML = '';
26
- if (!currentCommandId && commands.length) {
27
- currentCommandId = commands[0].command_id;
28
- viewOutput(currentCommandId);
29
- }
30
- commands.forEach(command => {
31
- const commandRow = document.createElement('tr');
32
- commandRow.className = `clickable-row ${command.command_id === currentCommandId ? 'currentcommand' : ''}`;
33
- commandRow.onclick = () => viewOutput(command.command_id);
34
- commandRow.innerHTML = `
35
- <td class="monospace">
36
- <span class="copy_clip" onclick="copyToClipboard('${command.command_id}', this, event)">${command.command_id.slice(0, 8)}</span>
37
- </td>
38
- <td><span class="status-icon status-${command.status}"></span>${command.status}</td>
39
- <td>${formatTime(command.start_time)}</td>
40
- <td>${command.status === 'running' ? formatDuration(command.start_time, new Date().toISOString()) : formatDuration(command.start_time, command.end_time)}</td>
41
- <td>${command.exit_code}</td>
42
- <td>${command.command.replace(/^\.\//, '')}</td>
43
- <td>
44
- ${command.status === 'running' ? `<button onclick="stopCommand('${command.command_id}')">Stop</button>` : `<button onclick="relaunchCommand('${command.command_id}')">Run</button>`}
45
- </td>
46
- <td class="monospace outcol">${command.last_output_line || ''}</td>
47
- `;
48
- commandsTbody.appendChild(commandRow);
49
- });
50
- }
51
-
52
- async function fetchExecutables() {
53
- const response = await fetch('/executables');
54
- const executables = await response.json();
55
- const commandNameSelect = document.getElementById('commandName');
56
- commandNameSelect.innerHTML = '';
57
- executables.forEach(executable => {
58
- const option = document.createElement('option');
59
- option.value = executable;
60
- option.textContent = executable;
61
- commandNameSelect.appendChild(option);
62
- });
63
- }
64
-
65
- async function fetchOutput(command_id) {
66
- const outputDiv = document.getElementById('output');
67
- const response = await fetch(`/command_output/${command_id}`);
68
- const data = await response.json();
69
- if (data.error) {
70
- outputDiv.innerHTML = data.error;
71
- clearInterval(outputInterval);
72
- } else {
73
- outputDiv.innerHTML = data.output;
74
- outputDiv.scrollTop = outputDiv.scrollHeight;
75
- if (data.status != 'running') {
76
- clearInterval(outputInterval);
77
- }
78
- }
79
- }
80
-
81
- async function viewOutput(command_id) {
82
- adjustOutputHeight();
83
- currentCommandId = command_id;
84
- clearInterval(outputInterval);
85
- const response = await fetch(`/command_status/${command_id}`);
86
- const data = await response.json();
87
- if (data.status === 'running') {
88
- fetchOutput(command_id);
89
- outputInterval = setInterval(() => fetchOutput(command_id), 1000);
90
- } else {
91
- fetchOutput(command_id);
92
- }
93
- fetchCommands(); // Refresh the command list to highlight the current command
94
- }
95
-
96
- async function relaunchCommand(command_id) {
97
- const response = await fetch(`/command_status/${command_id}`);
98
- const data = await response.json();
99
- if (data.error) {
100
- alert(data.error);
101
- return;
102
- }
103
- const relaunchResponse = await fetch('/run_command', {
104
- method: 'POST',
105
- headers: {
106
- 'Content-Type': 'application/json'
107
- },
108
- body: JSON.stringify({
109
- command: data.command,
110
- params: data.params
111
- })
112
- });
113
- const relaunchData = await relaunchResponse.json();
114
- fetchCommands();
115
- viewOutput(relaunchData.command_id);
116
- }
117
-
118
- async function stopCommand(command_id) {
119
- const response = await fetch(`/stop_command/${command_id}`, {
120
- method: 'POST'
121
- });
122
- const data = await response.json();
123
- if (data.error) {
124
- alert(data.error);
125
- } else {
126
- alert(data.message);
127
- fetchCommands();
128
- }
129
- }
130
-
131
- function formatTime(time) {
132
- if (!time || time === 'N/A') return 'N/A';
133
- const date = new Date(time);
134
- return date.toISOString().slice(0, 16).replace('T', ' ');
135
- }
136
-
137
- function formatDuration(startTime, endTime) {
138
- if (!startTime || !endTime) return 'N/A';
139
- const start = new Date(startTime);
140
- const end = new Date(endTime);
141
- const duration = (end - start) / 1000;
142
- const hours = Math.floor(duration / 3600);
143
- const minutes = Math.floor((duration % 3600) / 60);
144
- const seconds = Math.floor(duration % 60);
145
- return `${hours}h ${minutes}m ${seconds}s`;
146
- }
147
-
148
- function copyToClipboard(text, element, event) {
149
- event.stopPropagation();
150
- event.stopImmediatePropagation();
151
- navigator.clipboard.writeText(text).then(() => {
152
- element.classList.add('copy_clip_ok');
153
- setTimeout(() => {
154
- element.classList.remove('copy_clip_ok');
155
- }, 1000);
156
- });
157
- }
158
-
159
- function adjustOutputHeight() {
160
- const outputDiv = document.getElementById('output');
161
- const windowHeight = window.innerHeight;
162
- const outputTop = outputDiv.getBoundingClientRect().top;
163
- const maxHeight = windowHeight - outputTop - 30; // 20px for padding/margin
164
- outputDiv.style.maxHeight = `${maxHeight}px`;
165
- }
166
-
167
- function initResizer() {
168
- const resizer = document.getElementById('resizer');
169
- const tableContainer = document.getElementById('tableContainer');
170
- let startY, startHeight;
171
-
172
- resizer.addEventListener('mousedown', (e) => {
173
- startY = e.clientY;
174
- startHeight = parseInt(document.defaultView.getComputedStyle(tableContainer).height, 10);
175
- document.documentElement.addEventListener('mousemove', doDrag, false);
176
- document.documentElement.addEventListener('mouseup', stopDrag, false);
177
- });
178
-
179
- function doDrag(e) {
180
- tableContainer.style.height = `${startHeight + e.clientY - startY}px`;
181
- adjustOutputHeight();
182
- }
183
-
184
- function stopDrag() {
185
- document.documentElement.removeEventListener('mousemove', doDrag, false);
186
- document.documentElement.removeEventListener('mouseup', stopDrag, false);
187
- }
188
- }
189
-
190
- window.addEventListener('resize', adjustOutputHeight);
191
- window.addEventListener('load', () => {
192
- adjustOutputHeight();
193
- initResizer();
194
- });
195
-
196
- fetchExecutables();
197
- fetchCommands();
198
- setInterval(fetchCommands, 5000);
File without changes
File without changes
File without changes
File without changes
File without changes